https://kotlinlang.org logo
Title
k

koufa

02/09/2018, 1:22 PM
Hey. I have an question. (Simplified problem -->)
class A(val name: String, val country: String)
class B(val name: String, val age: Int)
class C(val name: String, val age: Int, val country: String)
What is the idiomatic way in Kotlin to combine two lists say
List<A>
and
List<B>
to a new
List<C>
if
C
is produced from
A and B
if
a.name == b.name
?
k

kristofdho

02/09/2018, 1:31 PM
probably sort both lists on name and iterate over both at the same time, but a number of conditions come to mind do both lists contain entries for the same names? what if they don't?
d

diesieben07

02/09/2018, 1:32 PM
val a = listOf<A>()
val b = listOf<B>()

val c = a.zip(b)
    .filter { (a, b) -> a.name == b.name}
    .map { (a, b) -> C(a.name, b.age, a.country) }
k

kristofdho

02/09/2018, 1:34 PM
zip combines elements by index, the number of cases where this works is limitted hence my questions
d

diesieben07

02/09/2018, 1:35 PM
Oh, yes that's true.
k

koufa

02/09/2018, 1:35 PM
They should contain similar names but not in the same order. If some don’t match then they can be ignored
d

diesieben07

02/09/2018, 1:37 PM
You probably need two
groupBy
then, get the set of shared keys of the two resulting maps, then combine
k

kristofdho

02/09/2018, 1:45 PM
fun toC(a: List<A>, b: List<B>): List<C> {
    val names = a.map(A::name).union(b.map(B::name))
    val aa = a.filter { names.contains(it.name) }.sortedBy(A::name)
    val bb = b.filter { names.contains(it.name) }.sortedBy(B::name)
    
    return aa.zip(bb).map { (a, b) -> C(a.name, b.age, a.country) }
}
not the cleanest for sure, also didn't test
b

bloder

02/09/2018, 1:48 PM
val listA = listOf(1, 2, 3, 4, 5)
        val listB = listOf(1, 2, 6, 7, 5)
        val listC = mutableListOf<Int>()
        listA.fold(listB) { acc, i -> if (acc.contains(i)) listC.add(i); acc }
print(listC) // [1, 2, 5]
you can use fold to integrate listA with listB and check what you want to add in listC
k

kristofdho

02/09/2018, 1:52 PM
fun toC2(a: List<A>, b: List<B>): List<C> {
    val aa = a.associateBy(A::name)
    val bb = b.associateBy(B::name)
    
    return aa.keys.union(bb.keys).map {
        C(it, bb[it]!!.age, aa[it]!!.country)
    }
}
pitty of the ugly
!!
@bloder that's a lot of expensive contains operations
a

Andreas Sinz

02/09/2018, 1:56 PM
@koufa I'd use
listA.mapNotNull { a -> 
    listB.firstOrNull { b -> a.name == b.name }
            ?.let { b -> C(a. name, b. age, a.country) }
}
k

kristofdho

02/09/2018, 1:59 PM
same as with the contains operation, firstOrNull can be expensive
a

Andreas Sinz

02/09/2018, 2:02 PM
@kristofdho right, it can be. he asked for the most idiomatic way, not the fastest 🙂 don't optimize the code prematurely
k

kristofdho

02/09/2018, 2:05 PM
wouldn't call this premature optimization tho, just proper use of datatypes
k

koufa

02/09/2018, 2:06 PM
Thank you guys I will check out your suggestions
j

juhanla

02/09/2018, 2:10 PM
@kristofdho’s proposal is a good way to do list merges and updates with specific unique keys like id's