1

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"   
    }
    ]
    }

    ]
}

1 Answer 1

1

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());
Sign up to request clarification or add additional context in comments.

2 Comments

what we need to do If we want to maintain the order? final output should be ordered(based on incremental sourceId). Response from the DB is already ordered based on sourceId?
If the list of responses is already sorted and this ordering should be maintained, the maps need to be collected using LinkedHashMap::new supplier. Otherwise (if the input is not pre-sorted, or another ordering is needed), sorting can be applied when collecting the result list. You may check the update.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.