Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Filter TreeMap of dates by maximum value of each month

I have a collection like below currently stored as a TreeMap for sorting. Note that each month has multiple entries. How can I use Java 8 streams to filter this by the max value in each month?

date=value
2010-01-01=2100.00, 
2010-01-02=2108.74, 
2010-02-01=2208.74, 
2010-02-02=2217.92, 
2010-03-01=2317.92, 
2010-03-02=2327.57, 
2010-04-01=2427.57, 
2010-04-02=2437.67, 
2010-05-01=2537.67, 
2010-05-02=2548.22, 
2010-06-01=2648.22, 
2010-06-02=2659.24, 
2010-07-01=2759.24, 
2010-07-02=2770.72, 
2010-08-01=2870.72, 
2010-08-02=2882.66, 
2010-09-01=2982.66, 
2010-09-02=2995.07, 
2010-10-01=3095.07, 
2010-10-02=3107.94, 
2010-11-01=3207.94, 
2010-11-02=3221.29
like image 392
Patrick Avatar asked Sep 27 '22 05:09

Patrick


1 Answers

A possible solution is the following:

  • create a Stream over all the entries in the Map
  • collect that Stream into a new Map where the key corresponds to the year-month part of the Map and the value is the current entry. In case of duplicates, only the maximum element with regard to the date will be kept
  • create a new Stream again on the values of that intermediate Map
  • and finally collect it into a TreeMap.

Assuming the initial Map is of type TreeMap<LocalDate, Double>, this would be an implementation (this code uses static imports from the Collectors class):

TreeMap<LocalDate, Double> filtered =
        map.entrySet()
           .stream()
           .collect(groupingBy(
                e -> YearMonth.from(e.getKey()), 
                collectingAndThen(maxBy(Map.Entry.comparingByKey()), Optional::get))
           )
           .values()
           .stream()
           .collect(toMap(
                Map.Entry::getKey, 
                Map.Entry::getValue,
                (v1, v2) -> { throw new IllegalStateException(); },
                TreeMap::new)
        );

In this code, the map is first grouped by the year-month using Collectors.groupingBy(classifier, downstream). The classifier returns a YearMonth object from the LocalDate. The downstream collector is used to collect all the values having the same year-month into a single value: in this case, we therefore use Collectors.maxBy(comparator) to select the maximum value according to the comparator comparing each entry LocalDate key (comparingByKey). Since this collector returns an Optional (in case the Stream is empty), we wrap it into a Collectors.collectingAndThen(downstream, finisher) where the finisher just returns the optional value. At the end of this step, we therefore have a Map<YearMonth, Map.Entry<LocalDate, Double>>.

Finally, we keep the values of this intermediate map to collect each entry into a new Map, where we explicitly create a TreeMap. Since we know there are no duplicates here, the merging function simply throws a IllegalStateException.

Sample input / output :

2010-01-01=2100.00
2010-01-02=2108.74
2010-02-01=2208.74
2010-02-02=2217.92
2010-03-01=2317.92
2010-03-02=2327.57
2010-04-01=2427.57

->

2010-01-02=2108.74
2010-02-02=2217.92
2010-03-02=2327.57
2010-04-01=2427.57
like image 126
Tunaki Avatar answered Sep 29 '22 08:09

Tunaki