Search code examples
javajava-streamgrouping

Group list of objects into Map<String, Boolean> with value being true if any object in group has field set as true using Java stream API


In a Java 17 project, I have a collection of objects with a String field propGroupName representing the group this object belongs to and a Boolean field propValActive representing whether this object is flagged as active internally.

I want to aggregate these objects by the string field into a Map<String, Boolean> with the key being the String field and the Boolean being false if all the booleans in the group are false and true if 1 or more of the booleans in the group are false. I have a working implementation with a simple for loop, but I want to know if there is a way to do this grouping through the Java Stream API, preferably in a way that short circuits? The goal is that I want to know of every group whether there are any objects in that group flagged as active.

I currently have this implementation which doesn't use the Streams API and doesn't short circuit:

public Map<String, Boolean> determineActiveGroups(
        HashMap<String, PropertyValueDefinitionGroupView> standardPvdgMap) {
    Map<String, Boolean> activeGroupsMap = new HashMap<String, Boolean>();
    for (PropertyValueDefinitionGroupView pvdgView : standardPvdgMap.values()) {
        if(pvdgView.getPropGroupOid() == null) {
            continue;
        }
        activeGroupsMap.putIfAbsent(pvdgView.getPropGroupName(), false);
        if(pvdgView.getPropValActive()) {
            activeGroupsMap.put(pvdgView.getPropGroupName(), true);
        }
    }
    return activeGroupsMap;
}

I have a different bit of code somewhere else that does something similar, but it retains the lists, and I managed to adapt something similar for what I need but I don't know what predicate I can use to finish it with. I assume it's going to use anyMatch, but I have no idea how to integrate it:

Map<String, Boolean> activeGroups = standardPvdgMap.values().stream()
    .collect(Collectors.groupingBy(PropertyValueDefinitionGroupView::getPropGroupName, ???????));

Solution

  • groupingBy is such a powerful collector :

    public Map<String, Boolean> determineActiveGroups(Map<String, PropertyValueDefinitionGroupView> standardPvdgMap) {
        return standardPvdgMap.values()
                .stream()
                .filter(pvdgView -> pvdgView.getPropGroupOid() != null)
                .collect(Collectors.groupingBy(
                        PropertyValueDefinitionGroupView::getPropGroupName,
                        Collectors.mapping(
                                PropertyValueDefinitionGroupView::getPropValActive,
                                Collectors.reducing(false, (a, b) -> a || b))
                ));
    }
    

    The trick is knowing that you can apply further collectors on the downstream. In this case I map to the flag, and then reduce the flags using the logical or.