Reputation: 151
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
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"}]
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
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