Welcome to 16892 Developer Community-Open, Learning,Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

I have data which arrives as such (list of maps):

{
    “name”: “Hello”,
    "source": “One”,
    "status": “Good”,
    “date”: "01-05-2021 7:35:58 PM"
},
{
    “name”: “Hello”,
    "source": “Two”,
    "status": “Good”,
    “date”: "01-05-2021 7:35:58 PM"
},
{
    “name”: “Goodbye”,
    "source": “Three”,
    "status": “Bad”,
    “date”: "01-05-2021 7:35:58 PM"
},
{
    “name”: “Goodbye”,
    "source": “Four”,
    "status": “Bad”,
    “date”: "01-05-2021 7:35:58 PM"
}

So I want to group this data by “name”, but also create a new field which collects the “source” and “status” fields into a list of objects. This would mean I'd have to map the inner data to a Java class as well (call these individual objects “sourceStatus” which I've already created a class for).

{
    “name”: “Hello”,
    “sourceStatuses”: [
        {
            “source”: ”One”,
            “status”: ”Good”
        },
        {
            “source”: ”Two”,
            “status”: ”Good”
        }
    ],
    “status”: “Good”,
    “date”: "01-05-2021 7:35:58 PM"
},
{
    “name”: “Goodbye”,
    “sourceStatuses”: [
        {
            “source”: ”Three”,
            “status”: ”Bad”
        },
        {
            “source”: ”Four”,
            “status”: ”Bad”
        }
    ],
    “status” : “Bad,
    “date”: "01-05-2021 7:35:58 PM"
}

I understand the groupingBy part can be done fairly straightforwardly with Java's Collector (https://www.baeldung.com/java-groupingby-collector), but I'm not sure how to achieve the resultant set for my use case, where I not only create a new field but am also collecting then mapping inner data to a class.

Edit: "date" and "status" are going to be the same for all items with the same "name".


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
3.4k views
Welcome To Ask or Share your Answers For Others

1 Answer

The key for the groupingBy collector should be another modifiable map without key "source" and the value should be a list of SourceStatus class, which can be collected using Collectors.mapping collector.

Note: Map's are used to represent a key and a final result deliberately.

Let's assume, SourceStatus has a custom constructor using Map<String, Object> as an argument to use it in the Collectors.mapping:

public class SourceStatus {
    private String source;
    private String status;

    public SourceStatus(Map<String, Object> map) {
        this.source = (String) map.get("source");
        this.status = (String) map.get("status");
    }
}

Then the code to get the resulting set of maps is as follows:

Set<Map<String, Object>> mapped = data
    .stream()
    .collect(Collectors.groupingBy(
        m -> {
            Map<String, Object> key = new LinkedHashMap<>();
            key.putAll(m);
            key.remove("source");
            return key;
        },
        Collectors.mapping(SourceStatus::new, Collectors.toList())
    ))
    .entrySet().stream()
    .map(e -> {
        e.getKey().put("sourceStatuses", e.getValue());
        return e.getKey(); 
    })
    .collect(Collectors.toCollection(LinkedHashSet::new)); // maintain insertion order
// convert to json
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(mapped);
System.out.println(json);

Output:

[ {
  "name" : "Hello",
  "date" : "01-05-2021 7:35:58 PM",
  "status" : "Good",
  "sourceStatuses" : [ {
    "source" : "1",
    "status" : "Good"
  }, {
    "source" : "2",
    "status" : "Good"
  } ]
}, {
  "name" : "Bye",
  "date" : "01-05-2021 7:35:58 PM",
  "status" : "Bad",
  "sourceStatuses" : [ {
    "source" : "3",
    "status" : "Bad"
  }, {
    "source" : "4",
    "status" : "Bad"
  } ]
} ]

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to 16892 Developer Community-Open, Learning and Share
...