2

I have a list of maps with simple Runner.class entities:

List<Map<String, Runner>> times

, for example:

{
   "pedro": [
      { lap=1, time=00:01:16.11 },
      { lap=2, time=00:00:42.45 },
      { lap=3, time=00:00:58.23 }
   ],
   "huan": [
      { lap=1, time=00:00:48.33 },
      { lap=2, time=00:00:41.21 }
   ]
   "gomez": [
      { lap=1, time=00:01:02.42 },
      { lap=2, time=00:01:12.31 },
      { lap=3, time=00:01:58.14 },
      { lap=3, time=00:00:55.41 }
   ]
}

Here is my runner class:

public class Runner {

   private Integer lap;
   private LocalTime time;
   // Getters and setters...
}

I need to get a map of statistics, like lambda summaryStatistics() does, of min, max and avg time for each runner, that looks like this:

Map<String, DoubleSummaryStatistics> statsMap

, so i could iterate it and use getMin(), getAverage() and getMax() methods... But I don't have any clue how to deal with LocalTime?

Naman
  • 27,789
  • 26
  • 218
  • 353
darth jemico
  • 605
  • 2
  • 9
  • 18
  • 1
    a custom collector is what you are after; but computing an average for two `LocalTime`s is going to be fun – Eugene Nov 12 '19 at 19:41
  • 2
    `Map>` makes no sense. Perhaps, you mean `Map`? One approach would be to map the `LocalTime` to a `long` value, gather the statistics and convert the min/max/avg values back to `LocalTime`. Alternatively, you may create your own statistics and collector implementation, similar to [this one](https://stackoverflow.com/a/51652539/2711488), just for `LocalTime` instead of `BigDecimal`. – Holger Nov 12 '19 at 20:20
  • Yeah, thanks, it was a mistype... The result should definitely be Map – darth jemico Nov 12 '19 at 20:32
  • The input type of your example(considering JSON) and the one defined in Java also contradict, the input type of the object should be `Map> times` instead of `List> times` – Naman Nov 13 '19 at 03:16
  • @Holger Did you mean to a `double` value instead of a `long` in your comment? and does using `toSecondOfDay` or `toNanoOfDay` make sense while trying to retrieve the summary values later? – Naman Nov 13 '19 at 03:28
  • 2
    @Naman depends on the point of view. The time classes only support conversions to int or long, depending on the field. But, of course, you can promote the result to `double`, even implicitly, to get the `DoubleSummaryStatistics`, if that’s what the OP wishes. I considered `long`, because converting result values back to a time looked natural to me. – Holger Nov 13 '19 at 07:42

1 Answers1

3

One of the ways to do the transformation as suggested in comments could be as follows:

public Map<String, DoubleSummaryStatistics> createStats(Map<String, List<Runner>> times) {
    return times.entrySet().stream()
          .collect(Collectors.toMap(Map.Entry::getKey,
                  e ->e.getValue().stream()
                          .map(Runner::getTime)
                          .mapToDouble(LocalTime::toSecondOfDay) // inverse while extrating summary details
                          .summaryStatistics()));
}
Naman
  • 27,789
  • 26
  • 218
  • 353
  • 1
    or `e ->e.getValue().stream() .collect(Collectors.summarizingDouble(r -> r.getTime() .toSecondOfDay())` or use a single `.mapToDouble(r -> r.getTime() .toSecondOfDay())` – Holger Nov 13 '19 at 07:45
  • 1
    @Holger single `map` was on my mind while writing the code. I just wanted to highlight the `toSecondOfday` and that a reverse approach would have to be applied to get the summary values. Thank you for responding and the `summarizingDouble` reminder. – Naman Nov 13 '19 at 08:05