33

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())));
1

4 Answers 4

38
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())));
Sign up to request clarification or add additional context in comments.

5 Comments

Wouldn't this group by Role and Name? i.e K1: Role V1: Batsman, Bowler K2: Name V2: player1, player2, player3
This doesn't work for the input. Updated question with sample Java code.
@user1692342 at the time of writing this answer I actually thought your map was like Map<String, String> x1 = Map.of( "Batsman", "Player1"); Map<String, String> y1 = Map.of( "Batsman", "Player2"); and so forth, looks like you've found the missing operation before i came back :) +1.
Yeah the main trick was using Collectors.tomapping within the collect. I wasn't aware of that part. Thanks for that!
@user1692342 you're welcome and update my answer for completeness.
23

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())));

Comments

19

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)

1 Comment

While this code may answer the question, providing additional context regarding why and/or how this code answers the question improves its long-term value.
0

Here is a generic approach to this problem. The groupBy method below takes 2 to 3 arguments.

  1. A collection of type List<E>
  2. A keyFn (key) of type Function<E, K>
  3. (Optional) A valueFn (value) of type Function<E, V> or simply E

I included some unit tests below.

CollectionUtils.java

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);
  }
}

CollectionUtilsTest.java

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"));
  }
}

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.