ThrowableException
ThrowableException

Reputation: 1426

Merge function of a map collector to add in list in Java 8

While creating a Map on a stream, I want to insert the values in the same object list for duplicate Keys.
I used Collectors.toMap(keyMapper, valueMapper, mergeFunction).
But I’m unable to write a correct merge function for the same keys.

Here is a working example code to show the exact problem.

Create a Java file named MapCollector and add the below code.

public class MapCollector {
    public static Map<ItemType, ItemRequest> breakRequestByItemType(final ItemRequest originalRequest) {
       return originalRequest
                .getItemStatuses()
                .stream()
                .filter(e->e.getType()!= null)
                .collect(Collectors.toMap(
                        ItemStatus::getType,
                        e->ItemRequest
                                .builder()
                                .prop1(originalRequest.prop1)
                                .prop2(originalRequest.prop2)
                                .itemStatuses(Arrays.asList(e))
                                .build()
                        //TODO: Need a merge function that will add this status to the list `itemStatuses` 

                ));
    }
}

@Data
@Builder
class ItemRequest{
    List<ItemStatus> itemStatuses;
    String prop1;
    String prop2;
}
enum ItemType{
    A,B,C
}
@AllArgsConstructor
@Getter
enum ItemStatus{
    A_STATUS_1("some desc1",ItemType.A),
    A_STATUS_2("some desc2",ItemType.A),
    B_STATUS_1("some desc3",ItemType.B),
    B_STATUS_2("some desc4",ItemType.B),
    C_STATUS_1("some desc5",ItemType.C);

    private final String desc;
    private final ItemType type;
}

Add the following dependencies in pom.xml for less verbose test code

 <dependencies>
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>1.18.24</version>
    </dependency>
    <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter</artifactId>
      <version>5.8.2</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.assertj</groupId>
      <artifactId>assertj-core</artifactId>
      <version>3.21.0</version>
    </dependency>
  </dependencies>

This is the test class showing the expected & test values

public class MapCollectorTest {

    @Test
    void testIt(){
        final ItemRequest originalRequest =  ItemRequest.builder()
                .itemStatuses(Arrays.asList(ItemStatus.A_STATUS_1,
                        ItemStatus.C_STATUS_1,
                        ItemStatus.B_STATUS_2,
                        ItemStatus.A_STATUS_2))
                .prop1("abc")
                .prop2("bcd")
                .build();

        Map<ItemType, ItemRequest> expectedMap = new HashMap<ItemType, ItemRequest>();
        expectedMap.put(ItemType.A, ItemRequest
                .builder()
                .itemStatuses(Arrays.asList(ItemStatus.A_STATUS_1,ItemStatus.A_STATUS_2))
                .prop1("abc")
                .prop2("bcd")
                .build());
        expectedMap.put(ItemType.B, ItemRequest
                .builder()
                .itemStatuses(Arrays.asList(ItemStatus.B_STATUS_1))
                .prop1("abc")
                .prop2("bcd")
                .build());
        expectedMap.put(ItemType.C, ItemRequest
                .builder()
                .itemStatuses(Arrays.asList(ItemStatus.C_STATUS_1))
                .prop1("abc")
                .prop2("bcd")
                .build());
        Assertions.assertThat(MapCollector.breakRequestByItemType(originalRequest))
                .containsAllEntriesOf(expectedMap);
    }
}

Upvotes: 2

Views: 2034

Answers (1)

Alexander Ivanchenko
Alexander Ivanchenko

Reputation: 28968

//TODO: Need a merge function that will add this status to the list itemStatuses

A merge function of toMap is a BinaryOperator which expects two arguments which are Values associated with the same Key, which should be resolved into a single value returned by the merge function.

A quote from the documentation:

mergeFunction - a merge function, used to resolve collisions between values associated with the same key, as supplied to Map.merge(Object, Object, BiFunction)

You need to extract a list itemStatuses from both values and combine them together using Collection.addAll, then return a value whose list has been modified.

(left, right) -> {
    left.getItemStatuses().addAll(right.getItemStatuses());
    return left;
}

Upvotes: 3

Related Questions