Home > OS >  Java-Stream - Grouping and Sorting based on aggregate count in Java 11
Java-Stream - Grouping and Sorting based on aggregate count in Java 11

Time:12-06

I have a Stream of custom objects as a parameter to a method. And obviously I can consume the Stream only once.

My object has a String attribute called category.

public class MyType {
    private String category;
    
    // other properties, getters, etc.
}

I need a sorted list of categories List<String>.

First, I need to sort the data based on the number of occurrences of each category. If the number of occurrences is the same, then sort such categories lexicographically.

Basically, I need something like this

java8 stream grouping and sorting on aggregate sum

but I don't have the luxury of consuming the stream more than once. So I don't know how to address this problem.

Here's an example:

Input:

{
    object1 :{category:"category1"},
    object2 :{category:"categoryB"},
    object3 :{category:"categoryA"},
    object4 :{category:"category1"},
    object5 :{category:"categoryB"},
    object6 :{category:"category1"},
    object7 :{category:"categoryA"}
}

Output:

List = {category1, categoryA, categoryB}

CodePudding user response:

You can generate a Map of frequencies for each category of type Map<String,Long> (count by category).

Then create a stream over its entries and sort them (by Value, i.e. by count, and Key, i.e. lexicographically), extract the category from each entry and store the result into a list.

Assuming that you have the following domain type:

public class MyType {
    private String category;
    
    // getters
}

Method generating a sorted list of categories might be implemented like this:

public static List<String> getSortedCategories(Stream<MyType> stream) {
    
    return stream.collect(Collectors.groupingBy(
            MyType::getCategory,
            Collectors.counting()
        ))
        .entrySet().stream()
        .sorted(
            Map.Entry.<String, Long>comparingByValue()
                .thenComparing(Map.Entry.comparingByKey())
        )
        .map(Map.Entry::getKey)
        .toList();
}
  • Related