combinedResult
is a stream of type ObjectNode that consists of data like this for example:
{"id":"id1","version":1,"Success":4,"Exception":6,"Failure":3} {"id":"id1","version":1,"Success":4,"Exception":6,"Failure":3} {"id":"id1","version":2,"Exception":1,"Success":2,"Failure":2} {"id":"id1","version":2,"Exception":1,"Success":2,"Failure":2}
I want to get a result like this:
{"id":"id1","version":1,"Success":8,"Exception":12,"Failure":6} {"id":"id1","version":2,"Success":4,"Exception":2,"Failure":4}
I have written the below BinaryOperator function
BinaryOperator<ObjectNode> func = (o1, o2) -> { if (o1.get("id").asText().equals(o2.get("id").asText()) && o1.get("version").equals(o2.get("version"))) { ObjectNode o = Jive.newObjectNode(); o.set("id", Jive.newJsonNode(o1.get("id"))); o.set("version", Jive.newJsonNode(o1.get("version"))); o.set("Success", Jive.newJsonNode(o1.get("Success").asInt() + o2.get("Success").asInt())); o.set("Failure", Jive.newJsonNode(o1.get("Failure").asInt() + o2.get("Failure").asInt())); o.set("Exception", Jive.newJsonNode(o1.get("Exception").asInt() + o2.get("Exception").asInt())); return o; } return o1; }; combinedResult.stream().reduce(func)
But when I try this out, I get below result:
Optional[{"id":"id1","version":1,"Success":8,"Failure":6,"Exception":12}]
I understand that this is because I return o1
as default value in the BinaryOperator, but I don’t know how to resolve this.
Advertisement
Answer
You can use reduce method.
<U> U reduce(U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner);
Your have start identity with a empty HashMap whose key will be unique identifier on which you wanted to cumulate results. (Id + Version)
public class JavaReduce { public static void main(String[] args) { //DataSetup List<ObjectNode> objectNodeList = List.of( new ObjectNode("id1", 1, 4, 6, 3), new ObjectNode("id1", 1, 4, 6, 3), new ObjectNode("id2", 2, 2, 1, 2), new ObjectNode("id2", 2, 2, 1, 2)); Map<String, ObjectNode> objectNodeCumulativeMap = objectNodeList.stream() .reduce(new HashMap<>(), (intermediate, ObjectNode) -> { String key = ObjectNode.getId().concat(String.valueOf(ObjectNode.getVersion())); if(!intermediate.containsKey(key)){ intermediate.put(key, ObjectNode); } else { ObjectNode objectNode = intermediate.get(key); objectNode.setSuccess(objectNode.getSuccess() + ObjectNode.getSuccess()); objectNode.setFailure(objectNode.getFailure() + ObjectNode.getFailure()); objectNode.setException(objectNode.getException() + ObjectNode.getException()); } return intermediate; }, (cumulative, intermediate) -> { cumulative.putAll(intermediate); return cumulative; }); System.out.println(objectNodeCumulativeMap.values()); } } //DTO for data @Data @AllArgsConstructor @NoArgsConstructor class ObjectNode { private String id; private Integer version; private Integer success; private Integer exception; private Integer failure; }
But ideal way as Sudipta Bhattacharyya and Holger mentioned is to use collect
Below is snippet of solution.
public class JavaCollect { public static void main(String[] args) { //DataSetup List<ObjectNode> objectNodeList = List.of( new ObjectNode("id1", 1, 4, 6, 3), new ObjectNode("id1", 1, 4, 6, 3), new ObjectNode("id2", 2, 2, 1, 2), new ObjectNode("id2", 2, 2, 1, 2)); Map<String, ObjectNode> collect = objectNodeList.stream() .collect(groupingBy(JavaCollect::uniqueKey, collectingAndThen(toList(), JavaCollect::downstream))); System.out.println(collect.values()); } private static ObjectNode downstream(List<ObjectNode> list) { ObjectNode objectNode = list.stream().findFirst().orElse(new ObjectNode()); objectNode.setSuccess(list.stream().map(ObjectNode::getSuccess).collect(summingInt(Integer::intValue))); objectNode.setException(list.stream().map(ObjectNode::getException).collect(summingInt(Integer::intValue))); objectNode.setFailure(list.stream().map(ObjectNode::getFailure).collect(summingInt(Integer::intValue))); return objectNode; } private static String uniqueKey(ObjectNode objectNode) { return objectNode.getId().concat(objectNode.getVersion().toString()); } }