Java - Merge objects of list given a condition

泄露秘密 提交于 2020-06-23 08:11:16

问题


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
        }]  

回答1:


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 Optionals 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.




回答2:


If the data is just Lists of Objects, then you should be able to merge the data fairly straight forward with a few loops. For example:

public static ArrayList<User> mergeData(ArrayList<User> userList) {
  ArrayList<User> users = new ArrayList<>(userList);

  for(int i = 0; i < users.size(); i++) {
      User currentUser = users.get(i);
      for(int j = i + 1; j < users.size(); j++) {
          User otherUser = users.get(j);

          if(currentUser.firstName.equals(otherUser.firstName) 
            && currentUser.lastName.equals(otherUser.lastName)
            && currentUser.type.equals(otherUser.type)) {
                //Apply the field merging
                currentUser.accountBalance += otherUser.accountBalance;
                if(currentUser.extraField == null) {
                    currentUser.extraField = otherUser.extraField;
                } else {
                    //Handle case where you pick whether to keep the value or not.
                }

                //Remove the merged data and move index back to account for removal
                users.remove(j);
                j--;
           }
      }
  }

  return users;
}

The code just runs through the values, compares it against every other remaining value in the list, merges where applicable, and then removes the merged object from the rest of the list before moving on to the next value.



来源:https://stackoverflow.com/questions/60415626/java-merge-objects-of-list-given-a-condition

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!