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)