Reputation: 5237
I have a List of Maps which stores the roles and the names of people. For ex:
List<Map<String, String>> listOfData
1) Role: Batsman
Name: Player1
2)Role: Batsman
Name: Player2
3)Role: Bowler
Name: Player3
Role and Name are the Keys of the map.
I want to convert this into a Map<String, List<String>> result
, which will give me a list of names for each role, i.e
k1: Batsman v1: [Player1, Player2]
k2: Bowler v2: [Player3]
listOfData
.stream()
.map(entry -> new AbstractMap.SimpleEntry<>(entry.get("Role"), entry.get("Name"))
.collect(Collectors.toList());
Doing this way will not give me a list of names for the role, it will give me a single name. How do i keep collecting the elements of the list and then add it to a key ?
Java code to create base structure:
Map<String, String> x1 = ImmutableMap.of("Role", "Batsman", "Name", "Player1");
Map<String, String> y1 = ImmutableMap.of("Role", "Batsman", "Name", "Player2");
Map<String, String> z1 = ImmutableMap.of("Role", "Bowler", "Name", "Player3");
List<Map<String, String>> list = ImmutableList.of(x1, y1, z1);
Map<String, List<String>> z = list.stream()
.flatMap(e -> e.entrySet().stream())
.collect(Collectors.groupingBy(Map.Entry::getKey,
Collectors.mapping(Map.Entry::getValue, Collectors.toList())));
Upvotes: 33
Views: 69328
Reputation: 48610
Here is a generic approach to this problem. The groupBy
method below takes 2 to 3 arguments.
collection
of type List<E>
keyFn
(key) of type Function<E, K>
valueFn
(value) of type Function<E, V>
or simply E
I included some unit tests below.
package org.example.util;
import static java.util.Collections.unmodifiableMap;
import static java.util.stream.Collectors.*;
import java.util.*;
import java.util.AbstractMap.SimpleEntry;
import java.util.Map.Entry;
import java.util.function.Function;
public class CollectionUtils {
public static <E, K, V> Map<K, List<V>> groupBy(
Collection<E> collection, Function<E, K> keyFn, Function<E, V> valueFn) {
return collection.stream()
.map(item -> new SimpleEntry<K, V>(keyFn.apply(item), valueFn.apply(item)))
.collect(groupingBy(Entry::getKey, mapping(Entry::getValue, toList())));
}
public static <E, K> Map<K, List<E>> groupBy(Collection<E> collection, Function<E, K> keyFn) {
return groupBy(collection, keyFn, Function.identity());
}
public static <K, V> Map<K, V> immutableMapOf(K k1, V v1, K k2, V v2) {
Map<K, V> mutableMap = new HashMap<>();
mutableMap.put(k1, v1);
mutableMap.put(k2, v2);
return unmodifiableMap(mutableMap);
}
}
package org.example.util;
import static java.util.Arrays.asList;
import static org.example.util.CollectionUtils.*;
import static org.junit.Assert.*;
import java.util.*;
import org.junit.Test;
import org.slf4j.*;
public class CollectionUtilsTest {
private static final Logger logger = LoggerFactory.getLogger(CollectionUtilsTest.class);
private static final List<Map<String, String>> data =
asList(
immutableMapOf("Username", "Batman", "Role", "Leader"),
immutableMapOf("Username", "Robin", "Role", "Subordinate"),
immutableMapOf("Username", "Superman", "Role", "Leader"));
@Test
public void testGroupBy() {
logger.info("Test groupBy(Collection<E>, Function<E, K>, Function<E, V>)");
Map<String, List<String>> grouped = groupBy(data, m -> m.get("Role"), m -> m.get("Username"));
logger.info("Checking keys...");
assertNotNull(grouped.get("Leader"));
assertNotNull(grouped.get("Subordinate"));
logger.info("Checking values...");
assertEquals("Batman", grouped.get("Leader").get(0));
assertEquals("Superman", grouped.get("Leader").get(1));
assertEquals("Robin", grouped.get("Subordinate").get(0));
}
@Test
public void testGroupBySimple() {
logger.info("Test groupBy(Collection<E>, Function<E, K>)");
Map<String, List<Map<String, String>>> grouped = groupBy(data, m -> m.get("Role"));
logger.info("Checking keys...");
assertNotNull(grouped.get("Leader"));
assertNotNull(grouped.get("Subordinate"));
logger.info("Checking values...");
assertEquals("Batman", grouped.get("Leader").get(0).get("Username"));
assertEquals("Superman", grouped.get("Leader").get(1).get("Username"));
assertEquals("Robin", grouped.get("Subordinate").get(0).get("Username"));
}
}
Upvotes: 0
Reputation: 191
The Collectors class provides convenience methods in the form of i.e. groupingBy which allow to group similar objects by a certain classifier. The classifier method is the input to that particular grouping function. This function will generate a Map with the respective classifier methods value as key and a list of objects that share the same classifier method value as value.
Therefore a code like
Map<String, List<Person>> roles2Persons =
lis.stream().collect(Collectors.groupingBy(Person::getRole));
will generate a mapping for the respective roles Person
objects may fulfill to a list of Person
objects that share the same role as the key in the map.
After the above collector was applied the resulting map will contain the desired form of
key1: Batsman, values: List(Player1, Player2)
key2: Bowler, values: List(Player3)
Upvotes: 19
Reputation: 56433
listOfData.stream()
.flatMap(e -> e.entrySet().stream())
.collect(Collectors.groupingBy(Map.Entry::getKey,
Collectors.mapping(Map.Entry::getValue,
Collectors.toList())));
update:
Slightly different variant to user1692342
's answer for completeness.
list.stream()
.map(e -> Arrays.asList(e.get("Role"), e.get("Name")))
.collect(Collectors.groupingBy(e -> e.get(0),
Collectors.mapping(e -> e.get(1), Collectors.toList())));
Upvotes: 38
Reputation: 5237
Based on the idea given by Aomine:
list.stream()
.map(e -> new AbstractMap.SimpleEntry<>(e.get("Role"), e.get("Name")))
.collect(Collectors.groupingBy(Map.Entry::getKey,
Collectors.mapping(Map.Entry::getValue, Collectors.toList())));
Upvotes: 23