Home > Mobile >  Join two lists with same type on a certain field in Kotlin (like inner join in SQL)
Join two lists with same type on a certain field in Kotlin (like inner join in SQL)

Time:03-12

The closest thing I could find is zip, which does pretty much what I want, but it uses the index. I want to specify a field on which the two lists get joined if they are the same value.

In SQL one would use "table1 INNER JOIN table2 WHERE table1.field = table2.field". Is there something similar in Kotlin?

Maybe this example makes it clearer:

class Something(val id : Int, val value : Int)

val list1 = listOf(Something(0, 1), Something(1, 6), Something(2, 8))
val list2 = listOf(Something(1, 2), Something(5, 3), Something(9, 6))

val result = list1.innerJoin(list2, on=id).map { element1, element2 -> element1.value * element2.value}
//should return [12] (6*2)

list1 and list2 both have an element with an id=1, so their values (6 and 2) get multiplied in this example and the result should be 12.

Currently I use this code snippet, which works, but I was wondering if there is an easier and more efficient way to do this.

val result = list1.map { element1 ->
    val element2 = list2.find { element2 -> element2.id == element1.id } ?: return@map null
    element1.value * element2.value
}.filterNotNull()

Thanks.

CodePudding user response:

You could use mapNotNull to eliminate the secondary filtering step, but that's about as concise as you can make it. You can also convert list2 to a Map first to change this from O(n^2) to O(n).

val list2ById = list2.associateBy(Something::id)
val result = list1.mapNotNull { element1 ->
    list2ById[element1.id]?.value?.times(element1.value)
}

CodePudding user response:

Kotlin doesn't provide stdlib method for this, but you can define your own:

fun <T> Collection<T>.innerJoin(other: Collection<T>, on: T.() -> Any): Collection<Pair<T, T>> {
    val otherByKey = other.associateBy(on)
    return this.mapNotNull {
        val otherMapped = otherByKey[on(it)]
        if (otherMapped == null) null else it to otherMapped
    }
}

Usage:

fun main() {
    val list1 = listOf(Something(0, 1), Something(1, 6), Something(2, 8))
    val list2 = listOf(Something(1, 2), Something(5, 3), Something(9, 6))
    val result = list1.innerJoin(list2, on = { id }).map { (element1, element2) -> element1.value * element2.value }
    println(result) //12
}
  • Related