Reputation: 1426
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
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 toMap.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