Is there a neat way of joining two different lists...
# arrow
k
Is there a neat way of joining two different lists of different types where they share the same attribute that they need to be matched on (like i id field) and then return a list of matching pairs?
y
associateBy
on one of them, then a
map
on the other, pairing it with the matching value from the first one
s
+1 to what @Youssef Shoaib [MOD] said, a code example:
Copy code
inline fun <A, B, C, Key> Iterable<A>.zipBy(
  other: Iterable<B>,
  keySelectorA: (A) -> Key,
  keySelectorB: (B) -> Key,
  combine: (A, B) -> C
): List<C> {
    val firstByKey = associateBy(keySelector)
    val secondByKey = other.associateBy(keySelector)
    return (firstByKey.keys intersect secondByKey.keys)
        .map { key -> firstByKey.getValue(it) to secondByKey.getValue(it) }
}
intersect
creates a
Set
of all common
Key
getValue
returns the value for
Key
, (and throws NoSuchKeyElementException but that is impossible in this case) • keySelector maps
A
and
B
to
Key
, in your case
id field
• combine allows to transform common value to be transformed in your case
::Pair
.
A more optimised implementation might be possible, but not sure if it's worth implementing.
y
Yeah I was thinking of only doing
associateBy
on one and iterate over the other with
mapNotNull
, but I think it's likely an unnecessary optimizatuon
🤔 1
s
Nope, that's definitely a lot better @Youssef Shoaib [MOD] 😄 Code is not more complex IMO.
Copy code
public inline fun <A, B, C, Key> Iterable<A>.zipBy(
  other: Iterable<B>,
  keySelectorA: (A) -> Key,
  keySelectorB: (B) -> Key,
  combine: (A, B) -> C
): List<C> {
  val aByKey = associateBy(keySelectorA)
  return other.mapNotNull { b ->
    val key = keySelectorB(b)
    val aOrNull = aByKey[key]
    aOrNull?.let { a -> combine(a, b) }
  }
}
❤️ 1