Search code examples
javajava-streamguava

Java stream merge and output


I have a list of objects of the form:

public class Child
{
    private Mom mom;
    private Dad dad;
    private String name;
    private int age;
    private boolean isAdopted;
}

I need to transform this list into a list of a different data structure, aggregating objects with the same Mom and Dad keys together into the form

public class Family
{
    private Mom mom;
    private Dad dad;
    private Map<String, int> kids;
}

where the 'kids' map is a map of all children names to ages.

Currently, I am doing this translation as follows:

public Collection<Family> transform( final Collection<Child> children )
{
    return children.stream()
                   .filter( child -> !child.getIsAdopted() )
                   .collect( ImmutableTable.toImmutableTable( child -> child.getMom(),
                                                              child -> child.getDad(),
                                                              child -> new HashMap<>(child.getName(), child.getAge() ),
                                                              (c1, c2) -> { 
                                                                  c1.getKids().putAll(c2.getKids());
                                                                  return c1;
                                                              } ) )
                   .cellSet()
                   .stream()
                   .map( Table.Cell::getValue)
                   .collect( Collectors.toList() );
}

Is there a way for me to do this without needing to collect to the intermediary table before transforming to the final list?


Solution

  • If you could define a GroupingKey with mom and dad attributes, the implementation could be simplified to:

    @Getter
    @AllArgsConstructor
    class GroupingKey {
        Mom mom;
        Dad dad;
    }
    
    public List<Family> transformer( final Collection<Child> children ) {
        return children.stream()
                .collect(Collectors.collectingAndThen(
                        Collectors.groupingBy(c -> new GroupingKey(c.getMom(), c.getDad())),
                        map -> map.entrySet().stream()
                                .map(e -> new Family(e.getKey().getMom(), e.getKey().getDad(),
                                        e.getValue().stream().collect(Collectors.toMap(Child::getName, Child::getAge))))
                                .collect(Collectors.toList())));
    }
    

    or if not by defining any other class, you can cast the objects with the same approach as :

    public List<Family> transform( final Collection<Child> children ) {
        return children.stream()
                .collect(Collectors.collectingAndThen(
                        Collectors.groupingBy(c -> Arrays.asList(c.getMom(), c.getDad())),
                        map -> map.entrySet().stream()
                                .map(e -> new Family(((Mom) ((List) e.getKey()).get(0)), ((Dad) ((List) e.getKey()).get(1)),
                                        e.getValue().stream().collect(Collectors.toMap(Child::getName, Child::getAge))))
                                .collect(Collectors.toList())));
    }