Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

GroupBy a list of strings

Tags:

java

I have this class:

class A {
    private List<String> keys;
    private String otherData;
    private int otherDate2;

    // getter and setters for each
}

For this class I have a simple list filled with some data. List<A> listOfA. Now I want to convert this data to a map. Map<String, List<A>>

Currently we using a bunch of methods to archive this in a very complicated way. I think, we can solve it with a simple stream()-operation.

I tried this

// first
listOfA.stream()
    .collect(Colletors.groupingBy(a -> a.getKeys()))
// produces a Map<String, List<A>>     

// second
listOfA.stream()
    .flatMap(a -> a.getKeys().stream())
    .collect(Colletors.groupingBy(string -> string))
// produces a Map<String, List<String>>

What is the right way for this situation?

Edit: To be clear, I want a Map<String, List<A>>.

like image 407
akop Avatar asked Sep 11 '18 14:09

akop


People also ask

How do you Groupby a list in Python?

You can group DataFrame rows into a list by using pandas. DataFrame. groupby() function on the column of interest, select the column you want as a list from group and then use Series. apply(list) to get the list for every group.


2 Answers

You don't need streams for this. It's easier this way:

Map<String, List<A>> result = new HashMap<>();

listOfA.forEach(a -> a.getKeys().forEach(key -> 
        result.computeIfAbsent(key, k -> new ArrayList<>()).add(a)));

This iterates outer and inner lists and fills a Map using computeIfAbsent, which creates an empty list if there is still no value for the given key, then A instances are simply added to the corresponding list.

like image 163
fps Avatar answered Oct 21 '22 22:10

fps


The first code will group by Map<List<String>, List<A>> and not Map<String, List<A>>.
The second code makes no sense : you group strings by themselves...

A simple way would be creating the set of all possible key-A couples.
You could use a Map for each couple but it looks like an overhead.
SimpleImmutableEntry that represents a key and value suits better. Once you get the whole couples, you can easily group A elements by key.

You could try something like :

import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.mapping;
import static java.util.stream.Collectors.toList;

    // ...
    List<A> listOfA = new ArrayList<>();
    listOfA.add(new A(Arrays.asList("1", "2"), "foo1", 1));
    listOfA.add(new A(Arrays.asList("2", "3"), "foo2", 2));
    listOfA.add(new A(Arrays.asList("3", "4"), "foo3", 3));

    Map<String, List<A>> map =
    listOfA.stream()
           .flatMap(a -> a.keys.stream()
                               .map(k -> new SimpleImmutableEntry<>(k, a)))
           .collect(groupingBy(e -> e.getKey(), mapping(e -> e.getValue(), toList())));

    map.forEach((k, v) -> System.out.println("key=" + k + ", value=" + v + "\n"));

Output :

key=1, value=[A [keys=[1, 2], otherData=foo1, otherDate2=1]]

key=2, value=[A [keys=[1, 2], otherData=foo1, otherDate2=1], A [keys=[2, 3], otherData=foo2, otherDate2=2]]

key=3, value=[A [keys=[2, 3], otherData=foo2, otherDate2=2], A [keys=[3, 4], otherData=foo3, otherDate2=3]]

key=4, value=[A [keys=[3, 4], otherData=foo3, otherDate2=3]]

like image 24
davidxxx Avatar answered Oct 21 '22 21:10

davidxxx