Reputation: 867
Given the following person definitions:
open class Person(val name: String, val age: Int)
object EmptyPerson:Person("", 0)
And the following data:
val left = listOf(Person("Tinky-winkey", 1), Person("lala", 2))
val right = listOf(Person("Dipsy", 3), Person("Tinky-winkey", 4))
Is it possible to zip theleft
and right
lists based on the name property to yields:
val result = listOf(
Pair(Person("Tinky-winkey", 1), Person("Tinky-winkey", 4)),
Pair(Person("lala", 2), EmptyPerson),
Pair(EmptyPerson, Person("Dipsy", 3))
)
In a way that keeps the left elements on the left side of the pair()
.
Something like left.zipByProperty {right, p->p.name}
Maybe zip is not the right way to go?
Upvotes: 0
Views: 1127
Reputation: 93759
zip
doesn't handle matching items by anything besides their position in the collection.
I think this would do what you're describing. It's best to create Maps first to keep the complexity down, unless these are always extremely short lists.
inline fun <T, K> zipBy(first: List<T>, second: List<T>, default: T, keySelector: (T)->K): List<Pair<T, T>> {
val firstByKey = first.associateBy(keySelector)
val secondByKey = second.associateBy(keySelector)
return (firstByKey.keys + secondByKey.keys)
.map { (firstByKey[it] ?: default) to (secondByKey[it] ?: default) }
}
Usage:
val result = zipBy(left, right, EmptyPerson, Person::name)
Upvotes: 4