Java 8 - List<Map> into Single Map [duplicate] - java

This question already has answers here:
Convert List of Maps to single Map via streams
(4 answers)
convert list of map to map using flatMap
(3 answers)
Create a map from a list of maps
(2 answers)
Closed 2 years ago.
I have a List<Map<String, String>>. I would like to convert it into single map.
The list size can be 1..n. Not sure how to do that using Java 8 Stream?
List<Map<String, String>> mapList = new ArrayList<>();
Map<String, String> map1 = new HashMap<>();
map1.put("1", "One");
map1.put("2", "Two");
Map<String, String> map2 = new HashMap<>();
map2.put("3", "Three");
map2.put("4", "Four");
mapList.add(map1);
mapList.add(map2);
Map<String, String> finalMap = new HashMap<>();
We could do something like this in Java 7:
for(Map<String, String> map : mapList) {
for(String key : map.keySet()) {
finalMap.put(key, map.get(key));
}
}
System.out.println("Final Map " + finalMap); // Final Map {1=One, 2=Two, 3=Three, 4=Four}

You can flatMap it and then use Collectors.toMap:
mapList.stream()
.flatMap(m -> m.entrySet().stream())
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
Note, that duplicate keys are not handled. To ignore duplicates, you can use:
Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (v1, v2) -> v1)

Here is one way to do it. I choose a method that handles duplicates by putting them in a list of strings. I added duplicate of each map in the other to demonstrate.
List<Map<String, String>> mapList = new ArrayList<>();
Map<String, String> map1 = new HashMap<>();
map1.put("1", "One");
map1.put("2", "Two");
map1.put("4", "Four");
Map<String, String> map2 = new HashMap<>();
map2.put("3", "Three");
map2.put("1", "One");
map2.put("4", "Four");
mapList.add(map1);
mapList.add(map2);
Map<String,List<String>> combined = mapList.stream()
.flatMap(m -> m.entrySet().stream())
.collect(Collectors.groupingBy(Entry::getKey,
Collectors.mapping(Entry::getValue,
Collectors.toList())));
combined.entrySet().forEach(System.out::println);
Prints
1=[One, One]
2=[Two]
3=[Three]
4=[Four, Four]

Related

How to remove innermap key using java 8 streams

I am trying to learn Java Stream API, I have the following code
List<HashMap<String, HashMap<String, String>>> map2 = new ArrayList<>();
HashMap<String, String> innerMap = new HashMap<>();
innerMap.put("a1", "hermione");
innerMap.put("a2", "harry");
innerMap.put("a3", "ron");
HashMap<String, HashMap<String, String>> outermap = new HashMap<>();
outermap.put("friends", innerMap);
List<HashMap<String, HashMap<String, String>>> list = new ArrayList<>();
list.add(outermap);
I want to remove harry from inner map and the same structure ( List<HashMap<String, HashMap<String, String>>>) should be retained, how do i achieve that using Streams API?
You really need to use java-stream here as long as you only want to remove a certain map entry while keeping the whole structure:
list.forEach(map -> map.forEach(
(key, value) -> value.entrySet()
.removeIf(entry -> "harry".equals(entry.getValue()))));
If you insist on using Stream API, be prepare for complicated handling with entries and dictionaries themselves as long as the Stream API is suitable to work with collections. Take a look:
List<Map<String, Map<String, String>>> newList = list.stream()
.map(outer -> outer.entrySet().stream()
.map(inner -> new SimpleEntry<>(
inner.getKey(),
inner.getValue().entrySet().stream()
.filter(entry -> !"harry".equals(entry.getValue()))
.collect(Collectors.toMap(Entry::getKey, Entry::getValue))))
.collect(Collectors.toMap(Entry::getKey, Entry::getValue)))
.collect(Collectors.toList());
Moreover, as already stated, the classes are the way more suitable to represent such complicated data structures.

Collect Map<String, Integer> from stream of Map<String, Map<String, Integer>>

I have a nested map with key as Employee name and values as another map with key as company name and value as years of experience like below
Map<String, Map<String, Integer>> map = new HashMap<>();
Map<String, Integer> innerMap1 = new HashMap<>();
innerMap1.put("INfosys", 2);
innerMap1.put("Volvo", 2);
innerMap1.put("MH", 3);
innerMap1.put("Piterion", 1);
Map<String, Integer> innerMap2 = new HashMap<>();
innerMap2.put("Tata", 2);
innerMap2.put("Bosch", 1);
innerMap2.put("Amber", 1);
innerMap2.put("E2", 1);
map.put("Rahul", innerMap1);
map.put("Amrita", innerMap2);
Now my function should return a Map with the employee name as key and total experience as value. How can I do that using java streams (in a single stream)
public Map<String, Integer> getEmployeesWithExp(Map<String, Map<String, Integer>> map) {
map.entrySet().stream().
...
return null;
}
There probably are multiple ways but you could collect the entries into a new map and reduce the values of the inner maps to integers, e.g. like this:
Map<String, Integer> result =
map.entrySet().stream()
.collect(
Collectors.toMap(e -> e.getKey(), //or Map.Entry::getKey
e -> e.getValue().values().stream()
.reduce(0, Integer::sum)));
This is the first time I tried to use streams with maps, it was quite a good exerxcise, thanks.
I failed to do it in only one stream, though. This solution features one main stream and internal streams.
I used org.apache.commons.lang3.tuple.Pair, by the way.
Map<String, Integer> result = map.entrySet().stream()
.map(entry -> Pair.of(entry.getKey(), entry.getValue().values().stream().reduce(0, Integer::sum)))
.collect(Collectors.toMap(Pair::getKey, Pair::getValue));
It answered
"Amrita" → 5
"Rahul" → 8
I believe it is correct. :D
This is simple for loops used for solution :-
Map<String, Integer> finalMap = new HashMap<>();
for (Entry<String, Map<String, Integer>> entry : map.entrySet()) {
Integer exp = 0;
for (Entry<String, Integer> entry2 : entry.getValue().entrySet()) {
exp += entry2.getValue();
}
finalMap.put(entry.getKey(), exp);
}
Output- {Amrita=5, Rahul=8}
Can be done in simple way:-
Function< Map<String, Integer>,Integer> sumOfValue = (x) -> x.values().stream().mapToInt(Integer::intValue).sum();
stringMapMap.entrySet().stream().collect(Collectors.toMap(
e-> e.getKey(), e-> sumOfValue.apply(e.getValue())
));

Filter List<Map<String, String>> using streams

I need to filter a list List<Map<String, String>> by value (if map have searched value, then map add to list) using streams. I'm trying to do this:
static Map<String, String> map1 = new HashMap<>();
static Map<String, String> map2 = new HashMap<>();
static Map<String, String> map3 = new HashMap<>();
static {
map1.put("key", "value");
map1.put("key2", "value2");
map2.put("key3", "value3");
map2.put("key2", "value2");
map3.put("key3", "value3");
map3.put("key4", "value4");
}
public static void main(String[] args) {
List<Map<String, String>> list = new ArrayList<>();
Map<String, String> resultMap = new HashMap<>();
list.add(map1);
list.add(map2);
list.add(map3);
List<Map<String, String>> result = list.stream()
.flatMap(map -> map.entrySet().stream())
.filter(value -> value.getValue().equals("value2"))
.map(x -> resultMap.put(x.getKey(), x.getValue()))
.collect(Collectors.toList());
}
For execution this code i have error:
java: incompatible types: inference variable T has incompatible bounds
equality constraints: java.util.Map
lower bounds: java.lang.String
If you want all the Maps of the input List that contain the "value2" value to appear in the output List, you need:
List<Map<String, String>> result =
list.stream()
.filter(map -> map.entrySet().stream().anyMatch (e->e.getValue().equals("value2")))
.collect(Collectors.toList());
Or (as Eritrean commented):
List<Map<String, String>> result =
list.stream()
.filter(map -> map.containsValue("value2"))
.collect(Collectors.toList());
Another quick solution is removeIf function. Has the advantage of reusing the same list object.
list.removeIf(map -> !map.containsValue("value2"));

How do I merge 2 HashMaps together? [duplicate]

This question already has answers here:
Merging 2 HashMaps in Java
(3 answers)
Closed 3 years ago.
I have 2 HashMaps of the type HashMap<String, Integer>. I would like to add them together in such a way that the values of duplicate keys get added together, rather than overwritten. This is the main reason why I can't use the putAll method for HashMaps. Is there a particular way I could do this easily?
You can use Map#merge e.g.
Map<String, Integer> map1 = new HashMap<>();
Map<String, Integer> map2 = new HashMap<>();
map1.put("a", 1);
map2.put("a", 2);
Map<String, Integer> map3 = new HashMap<>(map1);
map2.forEach((key, value) -> map3.merge(key, value, (v1,v2) -> v1+v2));
System.out.println(map3); // a=3
You can just use Stream.concat() to concatenate the streams of the two maps. Then you can collect them summing the duplicates:
Map<String, Integer> map1 = new HashMap<>();
map1.put("a", 2);
map1.put("b", 3);
Map<String, Integer> map2 = new HashMap<>();
map2.put("b", 1);
map2.put("c", 3);
Map<String, Integer> merged = Stream.concat(map1.entrySet().stream(), map2.entrySet().stream())
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (a, b) -> a + b));
Instead of (a, b) -> a + b you also can use Integer::sum.
The result for this would be:
{a=2, b=4, c=3}
You can use map merge from Java 8 :
public static void main(String[] args) {
Map<String, Integer> map1 = new HashMap<>();
map1.put("1", 1);
map1.put("2", 2);
Map<String, Integer> map2 = new HashMap<>();
map2.put("2", 2);
map2.put("3", 3);
map1.forEach((key, value) -> map2.merge(key, value, Integer::sum));
map2.forEach((s, integer) -> System.out.println(s + " " + integer));
}
Output is :
1 1
2 4
3 3
When it comes to making simple one-liners, the Stream API is your friend. I would use the Stream#collect method and the Collectors#toMap methods. For example:
Map<String, Integer> map1 = new HashMap<String, Integer>() {
{
put("first", 1);
put("second", 4);
put("third", 7);
}
};
Map<String, Integer> map2 = new HashMap<String, Integer>() {
{
put("fourth", 5);
put("fifth", 9);
put("third", 3);
}
};
Map<String, Integer> result = Stream.of(map1, map2).map(Map::entrySet).flatMap(Collection::stream)
.collect(Collectors.<Entry<String, Integer>, String, Integer>toMap(Entry::getKey, Entry::getValue,
(t, u) -> t + u));

Sort the values (Set -> SortedSet) of the Map with Java 8 Streams

How to sort values of the Map<String, Set<String>> i.e. convert to Map<String, SortedSet<String>> with streams?
Just iterate over each entry and convert the Set<T> (e.g. HashSet<T>) to a SortedSet<T> (e.g. TreeSet<T>) as:
Map<String, Set<String>> input = new HashMap<>();
Map<String, SortedSet<String>> output = new HashMap<>();
input.forEach((k, v) -> output.put(k, new TreeSet<>(v)));
or with streams as:
Map<String, Set<String>> input = new HashMap<>();
Map<String, SortedSet<String>> output = input.entrySet().stream()
.collect(Collectors.toMap(Map.Entry::getKey, a -> new TreeSet<>(a.getValue())));

Categories