I uttlerly convinced that my question its quite simple but im unable to do it with streams (if there is a way to do it without stream will be helpfull too) Suppose that we have this list of users
public class Users {
String firstName;
String lastName;
double accountBalance;
String type;
String extraField;
}
and suppose that we have the following data in my List < Users >
"Users": [{
"firstName": "Scott",
"lastName": "Salisbury",
"accountBalance": "100",
"type" : "A"
}, {
"firstName": "John",
"lastName": "Richards",
"accountBalance": "200",
"type" :"C"
}, {
"firstName": "John",
"lastName": "Richards",
"accountBalance": "200",
"type " : "C",
"ExtraField": "Apply"
}]
the expected result here its given that firstName, lastName and type appears twice on the list just merge the results that are common without missing any field
Expected output
"Users": [{
"firstName": "Scott",
"lastName": "Salisbury",
"accountBalance": "100",
"type" : "A"
}, {
"firstName": "John",
"lastName": "Richards",
"accountBalance": "400",//merged values
"type " : "C",
"ExtraField": "Apply" //value that remains in one object of the list
}]
You can create a key class containing the three fields, like
@Data
class UserKey {
String firstName;
String lastName;
String type;
static UserKey from(User user) { /* TODO (trivial) */ }
}
groupingBy
Those can be used to group your users
Map<UserKey,List<User>> grouped =
users.stream().collect(Collectors.groupingBy(UserKey::from));
Each of these lists can then be merged by
Optional<User> summed = userList.stream()
.collect(Collectors.reducing((u1, u2) -> {
u1.setAccountBalance(u1.accountBalance() + u2.accountBalance());
});
This can also be given directly as a downstream collector to the groupingBy
:
Map<UserKey,Optional<User>> mergedMap =
users.stream().collect(Collectors.groupingBy(UserKey::from,
Collectors.reducing((u1, u2) -> {
u1.setAccountBalance(u1.accountBalance() + u2.accountBalance());
return u1;
}));
Since those Optional
s are guaranteed to be filled, you can just call get()
on them; also, you don't need the keys anymore, so
List<User> result = mergedMap.values().stream()
.map(Optional::get)
.collect(toList());
toMap
As Naman suggested in the comments, you can also shortcut this by toMap
.
Map<UserKey,User> mergedMap = users.stream()
.collect(toMap(UserKey::from, Function.identity(),
(u1, u2) -> {
u1.setAccountBalance(u1.accountBalance() + u2.accountBalance());
return u1;
}));
List<User> result = new ArrayList<>(mergedMap.values());
Note that the reducing function has the side effect of manipulating one of the original user objects in the list, so make sure you don't need them again.