3

I have a following Person class

public class Person {

    public String name;

    public List<Brand> brands;

    //Getters
}

and a List<Person> persons(possibly with same names). I need to group in a map of <String, List<Brand>> with Person's name as Keys and lists of accumulated Brands as values.

Something like this

 Map<String, List<List<String>>> collect = list.stream().collect(
        groupingBy(Person::getName, mapping(Person::getBrands, toList()))
 );

produces undesired result and I know why. If the values could be somehow flatten during grouping? Is there a way to do it right there with Streams api?

srzhio
  • 186
  • 2
  • 12

5 Answers5

3

java 9 will add the flatMapping collector specifically for this type of task:

list.stream().collect(
    groupingBy(
        Person::getName, 
        flatMapping(
            p -> p.getBrands().stream(), 
            toList()
        )
    )
Misha
  • 27,433
  • 6
  • 62
  • 78
  • 1
    …and [this answer](https://stackoverflow.com/a/39131049/2711488) contains the necessary code to be able to use `flatMapping` in Java 8. It’s not that big… – Holger Sep 07 '17 at 08:43
1

You will need to merge brands into a single List:

list.stream().collect(Collectors.toMap(
            Person::getName,
            Person::getBrands,
            (left, right) -> {
                left.addAll(right);
                return left;
            },
            HashMap::new));
Eugene
  • 117,005
  • 15
  • 201
  • 306
  • 4
    Warning: This may inadvertently affect the list of brands in one of the `Person` objects. – Joe C Sep 06 '17 at 20:46
1

Guessing what is the desired result, you can achieve it with just toMap collector:

Map<String, List<String>> collect = persons.stream().collect(
    toMap(
            Person::getName,
            Person::getBrands,
            (l1, l2) -> ImmutableList.<String /*Brand*/>builder().addAll(l1).addAll(l2).build())
);
Piotr Findeisen
  • 19,480
  • 2
  • 52
  • 82
1

You can create a custom collector for the downstream to your groupBy:

Collector.of(LinkedList::new, 
            (list, person) -> list.addAll(person.brands), 
            (lhs, rhs) -> { lhs.addAll(rhs); return rhs; })
Joe C
  • 15,324
  • 8
  • 38
  • 50
  • Any reason for using `LinkedList` here? It’s not only inefficient for most tasks in general, it’s unsuited especially for this specific task. – Holger Sep 07 '17 at 08:46
0

There is MoreCollectors provided in open source library: StreamEx

list.stream().collect(
    groupingBy(Person::getName, MoreCollectors.flatMapping(p -> p.getBrands().stream()));
123-xyz
  • 619
  • 4
  • 5