Search code examples
javajava-streamcollectorsgroupingby

Java-Stream - Collect a List of objects into a Set of Strings after applying Collector groupingBy


Given the following classes Order and Item and a list of Orders.

@Getter
@AllArgsConstructor
@ToString
public class Order {
    String customerName;
    List<Item> items;
}

@Getter
@AllArgsConstructor
@ToString
@EqualsAndHashCode
public class Item {
    long id;
    String name;
}

I need to create a map Map<String,Set<String>> itemsByCustomerName where keys are customer names from Order and a set of names of all Items belonging to the customer name.

Input example:

List<Order> orders = List.of(
        new Order("John", List.of(new Item(1, "A"), new Item(2, "B"), new Item(3, "C"))),
        new Order("Alex", List.of(new Item(1, "A"), new Item(8, "H"), new Item(11, "K"))),
        new Order("John", List.of(new Item(1, "A"), new Item(6, "F"), new Item(7, "G"))),
        new Order("Alex", List.of(new Item(1, "A"), new Item(8, "H"), new Item(12, "L"))),
        new Order("Dave", List.of(new Item(24,"X"), new Item(25,"Y"), new Item(26, "Z"))));

the desired output would be

{Alex=[A, H, K, L], John=[A, B, C, F, G], Dave=[X, Y, Z]}

I have tried many ways including my last attempt below, but getting compile errors

Map<String, Set<String>> itemsByCustomerName =
    orders.stream().collect(groupingBy(
            Order::getCustomerName,
            mapping(order -> order.getItems().stream().map(Item::getName), toSet())));

Error:

Required type:
Map<String, Set<String>>
Provided:
Map<String, Set<Object>>

no instance(s) of type variable(s) R exist so that Stream<R> conforms to String inference variable T 
has incompatible bounds: equality constraints: String lower bounds: Stream<R81591>

How can I get instead of just a map of orders by name:

Map<String, List<Order>> ordersByName =
    orders.stream().collect(groupingBy(Order::getCustomerName));

to a Map<String, Set<Item>> or much better Map<String, Set<String>> with a set of item names?

So, my question is: how to convert a list of orders into a set of item names after applying groupingBy?


Solution

  • You were very close.

    Since you need to transform a stream element not into a single object, but extract a collection of items from the order, you need a different collector - flatMapping() instead of mapping().

    Map<String, Set<String>> itemsByCustomerName =
        orders.stream().collect(Collectors.groupingBy(
            Order::getCustomerName,
            Collectors.flatMapping(order -> order.getItems().stream().map(Item::getName),
                Collectors.toSet())));