Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use java 8 merge function for n number of hashmaps

I need to merge n hashmaps ideally in a loop like shown below using java 8 merge or something:

Map 1 : {Name:XXX,Phn:123,Work:""}

Map 2 : {Name:XXX,Phn:456,Work: xyz}

Map 3 : {Name:XXX,Phn:789,Work:""}

I would like to get an output like below :

{
   Name:XXX,        // if all values for a key are same take one
   Phn:123/456/789  // merge all non null values for same key
   Work:xyz         // make sure non-null values are never replaced
}

When I try to use putall like this

public Map<String,String> mergeOriginalDataMaps(List<Integer> indexList, List<Map<String,String>> originalData) {   
    Map<String,String> tmpMap = new HashMap<String,String> ();
    for (int index : indexList ) {
        tmpMap.putAll(originalData.get(index));
    }
    return tmpMap;
}

If the value of duplicate key is "", the previous value is replaced with the new one. I need to concatenate the values rather than replace them.

like image 688
nelz Avatar asked Mar 26 '16 20:03

nelz


People also ask

How do I combine two Hashmaps?

We can also use the putAll() method to merge two hashmaps. However, if a key is present in both hashmaps, the old value is replaced by the new value. Unlike the merge() , the putAll() method does not provide the remapping function. Hence, we cannot decide what value to store for duplicate keys.

How can I combine two HashMap objects containing the different types?

Assuming that both maps contain the same set of keys, and that you want to "combine" the values, the thing you would be looking for is a Pair class, see here for example. You simply iterate one of the maps; and retrieve values from both maps; and create a Pair; and push that in your result map.

What does the merge () method on map do?

The merge(Key, Value, BiFunctional) method of HashMap class is used to combine multiple mapped values for a key using the given mapping function.

Can we merge two maps?

Alternatively, we can use Stream#concat() function to merge the maps together. This function can combine two different streams into one. As shown in the snippet, we are passed the streams of map1 and map2 to the concate() function and then collected the stream of their combined entry elements.


1 Answers

If you have a List<Map<String, String>>, which represents a list of maps to merge, you can have

Map<String, String> result =
    maps.stream()
        .flatMap(m -> m.entrySet().stream())
        .collect(Collectors.groupingBy(
            Map.Entry::getKey,
            Collectors.mapping(
                Map.Entry::getValue,
                Collectors.collectingAndThen(Collectors.<String>toSet(), s -> String.join("", s))
            )
        ));

This flat maps each map into a Stream of its entries. Then the Stream is grouped by the value of each entry and all distinct elements with the same key are mapped to their value, joined.

The distinct part is made possible by first collecting all the values inside a Set.


Sample code:

public static void main(String[] args) {
    List<Map<String, String>> maps = new ArrayList<>();
    maps.add(new HashMap<String, String>(){{ put("Name", "XXX"); put("Phn", "123"); put("Work", ""); }});
    maps.add(new HashMap<String, String>(){{ put("Name", "XXX"); put("Phn", "456"); put("Work", "xyz"); }});
    maps.add(new HashMap<String, String>(){{ put("Name", "XXX"); put("Phn", "789"); put("Work", ""); }});

    Map<String, String> result =
        maps.stream()
            .flatMap(m -> m.entrySet().stream())
            .collect(Collectors.groupingBy(
                Map.Entry::getKey,
                Collectors.mapping(
                    Map.Entry::getValue,
                    Collectors.collectingAndThen(Collectors.<String>toSet(), s -> String.join("", s))
                )
            ));

    System.out.println(result); // prints "{Phn=123456789, Work=xyz, Name=XXX}"
}
like image 115
Tunaki Avatar answered Sep 29 '22 21:09

Tunaki