0

I have a list of summary rows, which has few-rows-per-entity, where some scalar properties of the entity are repeated and there are two additional columns GroupName and GroupCount which are unique.

Basically this is the output of a SQL join, and the entity data is repeated and there is a unique group name, and its count in each row.

I want to stream this and collect it into an entity Dto which has the entity properties as well as a Map for the merged group statistics.

I tried an implementation using Collectors.groupingBy, but it still doesn't look right.

 @Data @AllArgsConstructor public static class DepartmentSummaryRow{ private int id; private String name; private String groupName; private int groupMembersCount; } @Data @AllArgsConstructor public static class Department{ private int id; private String name; @EqualsAndHashCode.Exclude private final Map<String, Integer> groupCounts = new HashMap<>(); } public static void main(String[] args) { grouping(); } private static void grouping() { Gson g = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create(); //Test data List<DepartmentSummaryRow> summaries = new ArrayList<>(); for(int i=1;i<=50;i++) { summaries.add( new DepartmentSummaryRow(i, "name_a"+i, "g1", 3 ) ); summaries.add( new DepartmentSummaryRow(i, "name_b"+i, "g2", 9 ) ); } //Just group the summary rows Map<Department, List<DepartmentSummaryRow>> departmentsToSummaries = summaries .stream() .collect( Collectors.groupingBy( (summary)->{ return new Department(summary.id, summary.name); }, LinkedHashMap::new, Collectors.toList() ) ); //Merge the info into the departments departmentsToSummaries.forEach( (entity, sumaryRow)->{ entity.groupCounts.putAll( sumaryRow.stream().collect( Collectors.groupingBy( DepartmentSummaryRow::getGroupName, Collectors.summingInt( DepartmentSummaryRow::getGroupMembersCount ) ) ) ) ; } ); System.out.println( g.toJson( departmentsToSummaries.keySet() ) ); } 

I am looking for some ideas for a better implementation than this for grouping a stream into custom POJOs. Any suggestions would be helpful. Thanks!

(Note: this itself has some bug.. for some reason the first grouping by my POJO doesn't group at all.. which is odd, as it has a good hashcode and equals provided by Lombok)

Edit: Here's what the input looks like:

[ { "id": 1, "name": "name_a1", "groupName": "g1", "groupMembersCount": 3 }, { "id": 1, "name": "name_b1", "groupName": "g2", "groupMembersCount": 9 }, { "id": 2, "name": "name_a1", "groupName": "g1", "groupMembersCount": 3 }, ... ] 

And here's the expected result:

[ { "id": 1, "name": "name_a1", "groupCounts": { "g1": 3, "g2": 9 } }, { "id": 2, "name": "name_a2", "groupCounts": { "g1": 3, "g2": 9 } }, ... ] 
2
  • Can you show a sample of how the final json should look like? Commented Oct 19, 2021 at 14:29
  • @always_a_rookie Added Commented Oct 19, 2021 at 17:01

1 Answer 1

0

The main issue is that the expected result may be retrieved only if grouping is done by summary.id (summary.name values differ), then the name of the first matching DepartmentSummaryRow should be applied to remaining Department.

So a minor fix to exclude name from equals and hashCode in Department should do the trick:

@Data @AllArgsConstructor public static class Department { private int id; @EqualsAndHashCode.Exclude private String name; @EqualsAndHashCode.Exclude private final Map<String, Integer> groupCounts = new HashMap<>(); } 

However, it may be better to use Collectors.toMap with merge function and Supplier<Map> to achieve the similar result without using Department as a map key:

List<Department> result = new ArrayList<>( summaries .stream() // Stream<DepartmentSummaryRow> .collect(Collectors.toMap( DepartmentSummaryRow::getId, // int id as key SOGroup::create, // value: Department SOGroup::merge, // merge departments by id LinkedHashMap::new // keep insertion order )) .values() ); result.forEach(System.out::println); 

A couple of utility methods need to be implemented:

static Department create(DepartmentSummaryRow row) { Department dept = new Department(row.getId(), row.getName()); dept.getGroupCounts().put(row.getGroupName(), row.getGroupMembersCount()); return dept; } static Department merge(Department dept1, Department dept2) { dept2.getGroupCounts().forEach( (k, v) -> dept1.getGroupCounts().merge(k, v, Integer::sum) ); return dept1; } 

Output:

[ {"id":1,"name":"name_a1","groupCounts":{"g1":3,"g2":9}}, {"id":2,"name":"name_a2","groupCounts":{"g1":3,"g2":9}}, ... {"id":49,"name":"name_a49","groupCounts":{"g1":3,"g2":9}}, {"id":50,"name":"name_a50","groupCounts":{"g1":3,"g2":9}} ] 
Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.