Search code examples
javalambdajava-8java-streamcollectors

Groupby, reduce and sort for a list of objects


I have a List of Tags which has few properties like tagName, tagType, tagId, clicks, subscribers, views for each tag. tagName and tagType could be repeated for each of the tag objects while tagId is unique for each tag.

I want to perform an operation like this:

  1. Group/Aggregate list of tags by a combination of tagName and tagType
  2. For each group, I want to reduce the aggregated list to a single element for each combination of tagName and tagType. For the single element, tagId is irrelevant for me and could be even set to null or id of any tag object. However, I want to sum up clicks, subscribers and views in the single tag.
  3. Lastly, for the reduced aggregated single Tag Objects per tagName and tagType, I want to sort them based on criteria specified by user like clicks, subscribers or views and return top 5 Tag Objects.

To achieve this, my code snippet looks like this:

Map<TempClass, List<Tag>> aggregatedMap = tags.stream()
                        .collect(groupingBy(t -> new TempClass(t.getTagName(), t.getTagType()));

class Tag {
   String tagName;
   String tagType;
   String tagId;
   Long clicks;
   Long subscribers;
   Long views;
}

class TempClass {
   String tagName;
   String tagType;
}

Is there something which could be done with collectors in Java or I need to use for loops to achieve this ?


Solution

  •   public List<Tag> tags(List<Tag> input, long limit, Function<Tag, Long> extractor) {
        List<Tag> result =
                input
                        .stream()
                        .collect(Collectors.toMap(
                                x -> new TempClass()
                                        .setTagName(x.getTagName())
                                        .setTagType(x.getTagType()),
                                Function.identity(),
                                (left, right) -> {
                                    left.setClicks(left.getClicks() + right.getClicks());
                                    left.setSubscribers(left.getSubscribers() + right.getSubscribers());
                                    left.setViews(left.getViews() + right.getViews());
                                    return left;
                                }
                        ))
                        .values()
                        .stream()
                        .sorted(Comparator.comparing(extractor))
                        .limit(limit)
                        .collect(Collectors.toList());
    
        return result;
    }
    

    I've changed your TempClass a little:

    @EqualsAndHashCode
    @Getter
    @Setter
    @Accessors(chain = true)
    static class TempClass {
        String tagName;
        String tagType;
    }
    

    You need TempClass to have hashCode/equals based on tagName and tagType. The rest is not that complicated, imho.