How can I collect two-dimensional Array into a Map<String, Set<String>>?

Viewed 139

I have an array of arrays of two strings.

var array = new String[][]{
    {"a", "1"},
    {"a", "2"},
    {"b", "3"},
    ...
};

How can I collect the above array into a Map<String, Set<String>> whose key is the first element of each array and the value is a set of second elements of the array?

So that I get following map?

// Map<String, Set<String>>
<"a", ["1, "2"]>,
<"b", ["3"]>,
...

So far, I found I can classify the first element of each array like this.


Arrays.stream(array).collect(Collectors.groupingBy(
        a -> ((String[])a)[0],
        // how can I collect the downstream?
);
6 Answers

You can use Collectors.mapping() with a toSet() downstream:

Collectors.groupingBy(a -> a[0],
    Collectors.mapping(v -> v[1], Collectors.toSet()))

Which produces {a=[1, 2], b=[3]}. That first maps stream elements to the array's second element, then collects those as a set mapped to the group key.

Use Collectors.groupingBy in conjuction with Collectors.mapping().

public static void main(String[] args) {
    var array = new String[][]{
        {"a", "1"},
        {"a", "2"},
        {"b", "3"}
    };

    Map<String, Set<String>> result =
        Stream.of(array)
            .collect(Collectors.groupingBy(arr -> arr[0],
                Collectors.mapping(arr -> arr[1],
                    Collectors.toSet())));

    System.out.println(result);
}

Another way of doing this is to utilize Collector.of():

Map<String, Set<String>> result =
    Stream.of(array)
        .collect(Collector.of(
            HashMap::new,
            (Map<String, Set<String>> map, String[] arr) ->
                map.computeIfAbsent(arr[0], k -> new HashSet<>()).add(arr[1]),
            (Map<String, Set<String>> left, Map<String, Set<String>> right) ->
                { left.putAll(right); return left; }));

Output

{a=[1, 2], b=[3]}

You need a Collectors.mapping (also you don't need to specify String[] inside)

var array = new String[][]{{"a", "1"}, {"a", "2"}, {"b", "3"},};

Map<String, Set<String>> res = Arrays.stream(array).collect(Collectors.groupingBy(
        a -> a[0],
        Collectors.mapping(a -> a[1], Collectors.toSet())
));

System.out.println(res); // {a=[1, 2], b=[3]}

You can simply do this by using for-each loop

   Map<String, Set<String>> map=new HashMap<>();
   for(String []arr : array){
     Set<String> set = map.getOrDefault(arr[0], new HashSet<>());
     set.add(arr[1]);
     map.put(arr[0],set);
   }
   System.out.println(map);
 

Output :

{a=[1, 2], b=[3]}

Instead of Collectors.groupingBy, you could use Collectors.toMap.

public class Main {

  public static void main(String[] args) throws Exception {
    String[][] array = new String[][]{{"a", "1"}, {"a", "2"}, {"b", "3"}};
    Map<String, Set<String>> map = Arrays.stream(array).collect(Collectors.toMap(
            arr -> arr[0],
            arr -> new HashSet<>(Collections.singleton(arr[1])),
            (l, r) -> {
              l.addAll(r);
              return l;
            }));
    System.out.println(map);
  }
}

First parameter is keyMapper, second is valueMapper, third - mergeFunction.

You can loop through your array and for every character you can check if this key is saved before in your map, if yes just push the corresponding character to the list of that repeated key

Related