Issue
I have classes similar to:
class Response {
Source source;
Target target;
}
class Source {
Long sourceId;
String sourceName;
}
class Target {
Long regionId;
String regionName;
Long countryId;
String countryName;
}
In the Response, source(sourceId,sourceName) could be the same for different Target object.
Similarly, I also wants to make group within the Target Object based on regionId and regionName.
For combination of regionId and regionName, we can have List of countries within the Target Object.
I have entry in Database with these 6 properties sourceId, sourceName, targetId, targetName,countryId,countryName. I can have sourceId, sourceName same on multiple rows but target will always be different.
I want to group all the target objects into the List for which source is the same.
I have List of Response objects and I am trying to perform stream() operation on it something like:
List<Response> responses; // set up the input list
List<FinalResponse> finalResponseLst = responses.stream()
.collect(Collectors.groupingBy(
Response::getSource,
Collectors.mapping(Response::getTarget, Collectors.toList())
))
.entrySet()
.stream()
.map(e -> new FinalResponse(e.getKey(), e.getValue()))
.collect(Collectors.toList());
This is giving me Source with their respective Target Objects. But how to group countries within target objects based on their regions? How to create list of countries with same region for one single Target Object.
So that my final output JSON would look something like:
Response": {
"Source": {
"sourceId": "100",
"sourceName": "source1",
},
"Targets": [
{
"TargetRegion":{//grouping target objects with same regions
"regionId": "100",
"regionName": "APAC",
},
"TargetCountries":[{
"countryId": "1",
"countryName": "USA",
},
{
"targetId": "2",
"targetName": "China",
},
{
"targetId": "3",
"targetName": "Brazil"
}
]
},
{
"TargetRegion":{//grouping target objects with same regions
"regionId": "200",
"regionName": "ASPAC",
},
"TargetCountries":[{
"countryId": "11",
"countryName": "Japan",
},
{
"targetId": "22",
"targetName": "Combodia",
},
{
"targetId": "33",
"targetName": "Thailand"
}
]
}
]
}
Solution
You should just add extra grouping and make sure that appropriate POJO classes exist:
- POJO classes:
@Data
@AllArgsConstructor
class Region {
Long regionId;
String regionName;
}
@Data
@AllArgsConstructor
class Country {
Long countryId;
String countryName;
}
@Data
@AllArgsConstructor
class RegionCountries {
private Region targetRegion;
private List<Country> targetCountries;
}
@Data
@AllArgsConstructor
class FinalResponse {
Source source;
List<RegionCountries> targets;
}
Then collection may be implemented as follows.
If the input list responses is sorted and this ordering needs to be maintained, LinkedHashMap::new suppliers should be applied to all maps:
List<FinalResponse> f = responses.stream()
.collect(Collectors.groupingBy(
Response::getSource,
LinkedHashMap::new, // if pre-sorted by source
Collectors.groupingBy(
r -> new Region(
r.getTarget().getRegionId(),
r.getTarget().getRegionName()
),
LinkedHashMap::new, // if pre-sorted by region
Collectors.mapping(r -> new Country(
r.getTarget().getCountryId(),
r.getTarget().getCountryName()
),
Collectors.toList()
)
)
)) // Map<Source, Map<Region, List<Country>>>
.entrySet()
.stream()
.map(e -> new FinalResponse(
e.getKey(),
e.getValue().entrySet()
.stream()
.map(re -> new RegionCountries(re.getKey(), re.getValue()))
.collect(Collectors.toList()) // List<RegionCountries>
))
.collect(Collectors.toList());
If the input is not sorted or existing ordering needs to be changed, then upon collecting the intermediate map of maps, the stream of its entries may be re-ordered as necessary before collecting the resulting list (similarly when collecting RegionCountries):
// ... collecting the intermediate map as above
)) // Map<Source, Map<Region, List<Country>>>
.entrySet()
.stream()
.sorted(Map.Entry.comparingByKey(
Comparator.comparingLong(Source::getSourceId)
))
.map(e -> new FinalResponse(
e.getKey(),
e.getValue().entrySet()
.stream()
.sorted(Map.Entry.comparingByKey(
Comparator.comparingLong(Region::getRegionId)
))
.map(re -> new RegionCountries(re.getKey(), re.getValue()))
.collect(Collectors.toList()) // List<RegionCountries>
))
.collect(Collectors.toList());
Answered By - Nowhere Man
Answer Checked By - Terry (JavaFixing Volunteer)