7

I have some code that I need help with ... I'm trying to build a map using two maps as a source and at the same time using java lambdas

Map<String, List<String>> motorsMapping = new HashMap<>();

motorsMapping.put("CAR", Collections.singletonList("AUDI"));
motorsMapping.put("CAR_AND_BIKE", Arrays.asList("AUDI", "BMW"));
motorsMapping.put("BIKE", Collections.singletonList("BMW"));

Map<String, List<String>> models = new HashMap<>();
models.put("AUDI", Arrays.asList("A1", "Q2"));
models.put("BMW", Arrays.asList("X1", "X5"));


motorsMapping.keySet().forEach(key -> {
     Map<String, List<String>> result = new HashMap<>();
     result.put(key, new ArrayList<>());
     motorsMapping.get(key).forEach(value -> models.getOrDefault(value, Collections.emptyList()).forEach(property -> result.get(key).add(property)));
});

I can do that with the foreach that you can see above but I was trying to do it using lambdas ... something like this

Map<String, List<String>> collected = motorsMapping
    .entrySet()
    .stream()
    .map(entry -> {
        return entry.getValue()
            .stream()
            .map(key -> models.getOrDefault(key, Collections.emptyList()))
            .flatMap(List::stream)
            .collect(Collectors.groupingBy(entry.getKey(), Collectors.toList()));
    }
)...

My desired output would be something like

CAR -> A1, Q2
BIKE -> X1, X5
CAR_AND_BIKE -> A1, Q2, X1, X5

But I cannot get my head around it

Michiel Leegwater
  • 1,172
  • 4
  • 11
  • 27
F R
  • 93
  • 3

3 Answers3

3

As you've tagged this question under java-8 I'm assuming you have Java 8 and don't have access to flatMapping in Java 9. In java-8 you can do following using :Collectors.toMap

Map< String, List< String > > collected = motorsMapping.entrySet( )
            .stream( )
            .collect( Collectors.toMap( Map.Entry::getKey
                    , motorMap -> motorMap.getValue( )
                                    .stream( )
                                    .flatMap( value -> models.get( value ).stream( ) )
                                    .collect( Collectors.toList( ) ) ) );
collected.entrySet( ).stream( ).forEach( System.out::println );

Here's the output:

CAR_AND_BIKE=[A1, Q2, X1, X5]
CAR=[A1, Q2]
BIKE=[X1, X5]
stackFan
  • 1,528
  • 15
  • 22
1

There are many ways to achieve this. Here is one using the Java 9 methods Map.entry() and Collectors.flatMapping():

Map<String, List<String>> collected = motorsMapping.entrySet().stream()
        .flatMap(e -> e.getValue().stream()
                .map(i -> Map.entry(e.getKey(), models.get(i).stream())))
        .collect(Collectors.groupingBy(Map.Entry::getKey, 
                Collectors.flatMapping(Map.Entry::getValue, Collectors.toList())));

If you are limited to Java 8 you can use this approach:

Map<String, List<String>> collected = motorsMapping.entrySet().stream()
        .flatMap(e -> e.getValue().stream().flatMap(i -> models.get(i).stream())
                .map(i -> new AbstractMap.SimpleEntry<>(e.getKey(), i)))
        .collect(Collectors.groupingBy(Map.Entry::getKey,
                Collectors.mapping(Map.Entry::getValue, Collectors.toList())));

The last one uses AbstractMap.SimpleEntry() instead of Map.entry().

Both solutions use flatMap() to get the list of models for each key. After that the use flatMap() again to get the key for each model. Finally both use Collectors.groupingBy() to get the result:

{
  CAR_AND_BIKE: [A1, Q2, X1, X5],
  CAR: [A1, Q2], 
  BIKE: [X1, X5]
}
Samuel Philipp
  • 10,631
  • 12
  • 36
  • 56
1

The task could possibly be attained with something as simple as replaceAll such that:

Map<String, List<String>> result = motorsMapping;
result.replaceAll((vehicle, categories) -> categories.stream()
        .flatMap(category -> models.getOrDefault(category, Collections.emptyList()).stream())
        .collect(Collectors.toList()));
Naman
  • 27,789
  • 26
  • 218
  • 353