Jin Kwon
Jin Kwon

Reputation: 21978

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

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

Upvotes: 5

Views: 851

Answers (6)

Chaosfire
Chaosfire

Reputation: 6985

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.

Upvotes: 1

Saurabh Dhage
Saurabh Dhage

Reputation: 1711

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]}

Upvotes: 1

Alexander Ivanchenko
Alexander Ivanchenko

Reputation: 28988

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]}

Upvotes: 4

ernest_k
ernest_k

Reputation: 45319

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.

Upvotes: 4

azro
azro

Reputation: 54148

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]}

Upvotes: 2

Tarek Salah
Tarek Salah

Reputation: 19

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

Upvotes: 0

Related Questions