How do I sort map data by date and time and sum by date and / or hour of the day?

Suppose I have an integer data map where the keys are date and time in a format like:

May 15, 2015 17:46

So, for example, I have:

May 15, 2015 17:46, 25
May 15, 2015 17:50, 25
May 15, 2015 18:15, 30 May 15, 2015 20:05, 40
May 15, 2015 10:46 10 pm
May 15, 2015 5:10 am, 5 pm

Is there a way to sort the data by hour? So I can have something like:

5pm, 55
6pm 30
8PM, 40
10PM, 10

and also the same for dates?

+3


source to share


3 answers


Map<String, Integer> originalMap = ... // the map you mentioned
Map<String, Integer> aggregationMap = new HashMap<String, Integer>();
DateFormat sdf = new SimpleDateFormat("MMM dd, yyyy hh:mm a", Locale.US);

// change "ha" to "yyyyMMdd" if you wanna do aggregation by date
DateFormat sdf2 = new SimpleDateFormat("ha", Locale.US);
Iterator<String> it = originalMap.keySet().iterator();
while (it.hasNext()) {
    String k = it.next();
    int v = originalMap.get(k);
    String key = sdf2.format(sdf.parse(k));
    Integer value = aggregationMap.get(key);
    if (value == null) {
        aggregationMap.put(key, v);
    } else {
        aggregationMap.put(key, v + value);
    }
}

// TODO dump aggregationMap to see the result

      



+2


source


The best way I can do this, assuming you are using an implementing class java.util.Map

, would be:

  • Get a set of map records using the method entrySet()

    .
  • Write a custom class Comparator

    to sort the date and time records by hour in ascending order. Separately, you can write one to sort by date.
  • Create a TreeSet (implementation SortableSet

    ) using the constructor that initializes the TreeSet with a custom one Comparator

    .
  • Use TreeSet.addAll()

    to copy and sort the entries from those Set<Map.Entry>

    obtained in step 1.


Alternatively, if you don't want to create a data structure that is an exact copy of your existing map, you can only get keySet

and sort the keys. Then when you want to access the elements of the map in order, you can iterate through the sorted one keySet

and output the corresponding numbers from the existing map.

0


source


First, I'm going to assume that your original data is stored in Map<String, Integer>

, which is a named member field rawData

. Using Java 8, here is the gist of your desired aggregate pivot and sorting logic:

<T extends Comparable<? super T> & Temporal> SortedMap<T, Integer> summarize(
    DateTimeFormatter keyFormat,
    Function<LocalDateTime, ? extends T> keyTransform)
{
    SortedMap<T, Integer> summaryData = new TreeMap<>();

    rawData.forEach((k, v) -> {
        LocalDateTime rawDateTime = LocalDateTime.parse(k, keyFormat);
        T summaryKey = keyTransform.apply(rawDateTime);
        summaryData.merge(summaryKey, v, (x, y) -> x + y);
    });

    return summaryData;
}

      

This method processes a map rawData

and returns a sorted map whose keys are of type Temporal

- for example, LocalDate

no time component or LocalTime

no date component. To determine which behavior you must pass two arguments:

  • A format object that describes how to interpret the String

    raw data key as LocalDateTime

    .
  • A transform function that will then convert LocalDateTime

    to your aggregate keys at the desired level of granularity.

The call summaryData.merge(...)

will take care of adding the key and value the first time; when the same key is concatenated again, the provided lambda expression concatenates the previous and new values ​​- in your case, you just want to add two.

The key type (for example LocalDate

) must also be Comparable

, so the sorted map implementation will automatically sort. Here is the helper method that the above uses to generate the desired result in your question, by the hour of the day. The raw data key formatter is still required as input:

void printSummaryByHour(DateTimeFormatter rawKeyFormat)
{
    SortedMap<LocalTime, Integer> summary =
        summarize(
            rawKeyFormat,
            key -> LocalTime.of(key.getHour(), 0) // ignore date, minutes, seconds, etc.
        );

    DateTimeFormatter summaryKeyFormat =
        DateTimeFormatter.ofPattern("h a", Locale.US); // e.g. "7 PM"
    print(summary, summaryKeyFormat::format);
}

      

The method call summarize

uses a lambda expression to display LocalDateTime

in LocalTime

(drop date), which always ignores the minute value and only stores the hour. It then passes the resulting sorted set to a method, which I will later consider for output to the console, but in all likelihood your real program will want to do more than print to the screen.

Your question did not provide an example of the expected result "by date", so I am assuming that you want discrete days such as "May 15, 2015" and not an aggregate over several months or something special - just sum the whole 24 -hour periods. It is easy to do this with a different version of the above method, changing only the pivot key type, pivot conversion, and output formats:

void printSummaryByDate(DateTimeFormatter rawKeyFormat)
{
    SortedMap<LocalDate, Integer> summary =
        summarize(
            rawKeyFormat,
            LocalDateTime::toLocalDate
        );

    DateTimeFormatter summaryKeyFormat =
        DateTimeFormatter.ofPattern("MMMM dd, yyyy", Locale.US); // e.g. "May 15, 2015"
    print(summary, summaryKeyFormat::format);
}

      

The sorted map is now used LocalDate

instead of LocalTime

. Key conversion from datetime to date only uses a function reference instead of a custom lambda expression, and the output format of the pivot keys changes accordingly.

To answer this question, follow these steps: Installation scaffolding and helper method print

:

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.Temporal;
import java.util.*;
import java.util.function.Function;

public class StackOverflow30494397
{
    private final Map<String, Integer> rawData = new HashMap<>();

    public static void main(String... args)
    {
        StackOverflow30494397 instance = new StackOverflow30494397();
        instance.rawData.put("May 15, 2015 5:46 PM", 25);
        instance.rawData.put("May 15, 2015 5:50 PM", 25);
        instance.rawData.put("May 15, 2015 6:15 PM", 30);
        instance.rawData.put("May 15, 2015 8:05 PM", 40);
        instance.rawData.put("May 15, 2015 10:46 PM", 10);
        instance.rawData.put("May 15, 2015 5:10 AM", 5);

        DateTimeFormatter rawKeyFormat =
            DateTimeFormatter.ofPattern("MMMM dd, yyyy h:mm a", Locale.US);

        instance.printSummaryByHour(rawKeyFormat);
        instance.printSummaryByDate(rawKeyFormat);
    }

    private <K> void print(Map<K, ?> map, Function<? super K, String> keyFormatter)
    {
        map.forEach((k, v) -> System.out.printf("%s, %d%n", keyFormatter.apply(k), v));
        System.out.println();
    }

    // INSERT OTHER METHODS (DESCRIBED IN ANSWER) HERE    
}

      

Outputs:

5 am, 5
   5 pm, 50
   6 pm, 30
   8 pm, 40
   10 pm, 10

May 15, 2015, 135

0


source







All Articles