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

Time:05-03

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

CodePudding user response:

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.

CodePudding user response:

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

CodePudding user response:

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

CodePudding user response:

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

CodePudding user response:

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.

CodePudding user response:

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