Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java 8 Merge maps in iterator

I have an iteraror where in every iteration I´m creating a new map

Map<String, List<String>>

Now I would like to merge in every iteration the last emitted map with the new one.

If I send a list of items to getMap

{"a","a","b"}

I expect to receive a map of

["a",{"foo:a", "foo:a"}, "b",{"foo:b"}]

I try to use reduce function, but because putall only works if I use multimap and not map, is not a good option.

Here my code

public Map<String, List<String>> getMap(List<String> items){
       return items().stream()
                .map(item -> getNewMap(item) --> Return a Map<String, List<String>>
                .reduce(new HashMap<>(), (o, p) -> {
                    o.putAll(p);
                    return o;
                });
}

public Map<String, List<String>> getNewMap(String item){
    Map<String, List<String>> map = new HashMap<>();
    map.put(item, Arrays.asList("foo:" + item));
    return map;
}       

I´m looking for a no verbose way to do it.

like image 231
paul Avatar asked May 22 '26 19:05

paul


2 Answers

What you want is to flat map each intermediate map to its entries and make a single map out of that.

In the following code, each item is mapped to its corresponding map. Then, each map is flat mapped to its entries and the Stream is collected into a map.

public static void main(String[] args) {
    System.out.println(getMap(Arrays.asList("a", "a", "b")));
    // prints "{a=[foo:a, foo:a], b=[foo:b]}"
}

public static Map<String, List<String>> getMap(List<String> items) {
    return items.stream()
                .map(item -> getNewMap(item))
                .flatMap(m -> m.entrySet().stream())
                .collect(Collectors.toMap(
                    Map.Entry::getKey,
                    Map.Entry::getValue,
                    (l1, l2) -> { List<String> l = new ArrayList<>(l1); l.addAll(l2); return l; }
                ));
}

public static Map<String, List<String>> getNewMap(String item) {
    Map<String, List<String>> map = new HashMap<>();
    map.put(item, Arrays.asList("foo:" + item));
    return map;
}

In the case of multiple keys, this appends each list together.

like image 167
Tunaki Avatar answered May 25 '26 07:05

Tunaki


Whenever you want to get a Map<…, List<…>> from a stream, you should first check, how the groupingBy collector fits in. In its simplest form, it receives a grouping function which determines the keys of the resulting map and will collect all elements of a group into a list. Since you want the prefix "foo:" prepended, you’ll have to customize this group collector by inserting a mapping operation before collecting the items into a list:

public static Map<String, List<String>> getMap(List<String> items) {
    return items.stream().collect(Collectors.groupingBy(
        Function.identity(),
        Collectors.mapping("foo:"::concat, Collectors.toList())));
}

The classification function itself is as trivial as the identity function, as you want all equal elements building one group.

like image 27
Holger Avatar answered May 25 '26 08:05

Holger



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!