Search code examples
dictionaryjava-streamaverage

How to get the average duration for each category having films using stream API java


I have a class Film with the following fields

      String name;
      double duration;
      Collection<String> categories;

My task is to return a Map<String, Double> that represents the average duration by category. I have to use Stream API of Java.

My idea is to form entries of String for the category and List<Double> where the elements are the different durations, and then to use stream api method map and map it to map of string and average. But I am not sure how to do it. Can someone help me how to do it?


Solution

  • You can use the three-argument form of Collectors.groupingBy() with a downstream Collectors.averagingDouble() collector to do it in a single pass. Java 17 example (Including using Map.Entry to emulate a 2-element tuple or pair class):

    import java.util.*;
    import java.util.stream.*;
    
    public class Demo {
        private record Film(String name, double duration,
                            Collection<String> categories) {}
    
        public static void main(String[] args) {
            var films = List.of(new Film("The Terminator", 1.783,
                                         List.of("Sci-Fi", "Action")),
                                new Film("Alien", 1.95,
                                         List.of("Sci-Fi", "Horror")));
    
            Map<String,Double> avgDurations = films.stream()
                // Get a stream of (Category, Duration) pairs
                .flatMap(film ->
                         film.categories().stream()
                         .map(cat -> Map.entry(cat, film.duration())))
                // Group by Category and calculate the average of each
                // group's Durations in a TreeMap
                .collect(Collectors.groupingBy(Map.Entry::getKey,
                                               TreeMap::new,
                                               Collectors.averagingDouble(Map.Entry::getValue)));
    
            System.out.println(avgDurations);
        }
    }
    

    When compiled and run, prints out

    {Action=1.783, Horror=1.95, Sci-Fi=1.8664999999999998}