Gustavo Trevisani
Gustavo Trevisani

Reputation: 151

How to Merge two or more Objects in a ObjectsList

I have would like to know if there is a way to merge two ( or more ) objects in one list.

Exemple:

I have this class:

class User {                
    String name
    Integer age
    Integer score
}

and I got this method on another class

methodTest() {
    User a = new User().with{ 
        it.name = "JACK"
        it.age = 20
    }
    User b = new User().with{
        it.name = "JACK"
        it.score = 50
    }
    User c = new User().with{ 
        it.name = "TONY"
        it.age = 25
    }
    User d = new User().with{
        it.name = "TONY"
        it.score = 30
    }
    List userList = new ArrayList()
    userList.add(a)
    userList.add(b)
    userList.add(c)
    userList.add(d)
}

Tere is a way to get a userList merged by name? Something like :

userList = userList.mergeBy(it.name)

and then get a list of Users with:

[{name:"Jack", age: 20 , score: 50},{name:"TONY", age: 25, score: 30}]

Upvotes: 1

Views: 971

Answers (2)

Szymon Stepniak
Szymon Stepniak

Reputation: 42184

You can use .groupBy to group your list by User.name and then transform it to a List<User> by applying .inject function. Below you can find an example (fixed version the code you have shown us):

import groovy.json.JsonOutput

class User {
    String name
    Integer age
    Integer score
}

User a = new User(name: "JACK", age: 20)
User b = new User(name: "JACK", score: 50)
User c = new User(name: "TONY", age: 25)
User d = new User(name: "TONY", score: 30)

List userList = new ArrayList()
userList.add(a)
userList.add(b)
userList.add(c)
userList.add(d)

List<User> users = userList.groupBy { it.name } // (1)
        .values() // (2)
        .inject([]) { result, users -> // (3)
            result << users.inject(new User()) { User merged, User user -> // (4)
                merged.name = user.name ?: merged.name
                merged.age = user.age ?: merged.age
                merged.score = user.score ?: merged.score
                return merged
            }
}

println JsonOutput.toJson(users)

Let's see what happens here step-by-step:

  • (1) userList.groupBy { it.name } produces following map:

    [JACK:[User(JACK, 20, null), User(JACK, null, 50)], TONY:[User(TONY, 25, null), User(TONY, null, 30)]]
    
  • (2) calling .values() on this map returns a list of list of users:

    [[User(JACK, 20, null), User(JACK, null, 50)], [User(TONY, 25, null), User(TONY, null, 30)]]
    
  • (3) then .inject([]) { result, users -> /* ... */ } collects every list of users, applies transformation and adds result to result list (we start with empty [] here)

  • (4) here we call another .inject() function on users list (this users list contains a list of users with same name, e.g. [JACK:[User(JACK, 20, null), User(JACK, null, 50)]). We start with a new "empty" user (.inject(new User())). We access it by merged variable inside the closure - this variable holds the last result of each iteration inside .inject() function. So it starts with this empty user, gets the first one, sets the name and age (score is not set, because it is null), then it gets second user, sets name (same one) and score (age is not set, because in this user has null age). Final User is added to result list using left shift operator <<.

Eventually when you print to console your final users list you will see desired output:

[{"age":20,"score":50,"name":"JACK"},{"age":25,"score":30,"name":"TONY"}]

Final note

Of course you can make this code even simple, e.g. you can add a method to User class that merges two user instances, something like:

import groovy.json.JsonOutput

class User {
    String name
    Integer age
    Integer score

    User merge(User user) {
        return new User(
                name: user.name ?: name,
                age: user.age ?: age,
                score: user.score ?: score
        )
    }
}

List<User> userList = [
        new User(name: "JACK", age: 20),
        new User(name: "JACK", score: 50),
        new User(name: "TONY", age: 25),
        new User(name: "TONY", score: 30)
]

List<User> users = userList.groupBy { it.name }
        .values()
        .inject([]) { result, users ->
            result << users.inject(new User()) { User merged, User user -> merged.merge(user) }
        }

println JsouOutput.toJson(users)

Upvotes: 1

injecteer
injecteer

Reputation: 20699

Using some simple groovy magic:

class User{  
   String name
   Integer age
   Integer score
   String toString(){ "$name:$age:$score" }
} 

        User a = new User(
            name:"JACK",
            age : 20
        )
        User b = new User(
            name : "JACK",
            score :50
        )
        User c = new User(
            name : "TONY",
            age : 25
        )
        User d = new User(
            name : "TONY",
            score : 30
        )
        List userList = [ a, b, c, d ]

 def mergedList = userList.inject( [:].withDefault{ new User() } ){ res, User u ->
   res[ u.name ].name = u.name
   if( u.age ) res[ u.name ].age = u.age
   if( u.score ) res[ u.name ].score = u.score
   res
 }.values()

 assert '[JACK:20:50, TONY:25:30]' == mergedList.toString()

Upvotes: 0

Related Questions