Is there a better way to intersect two lists by a ...
# getting-started
h
Is there a better way to intersect two lists by a given field?
Copy code
fun <T, K> List<T>.intersectBy(new: List<T>, keySelector: (K)): List<T> =
    map { keySelector to it }
    .intersect(new.map { keySelector to it }.toSet())
    .map { (_, value) -> value }
s
🤔 is
keySelector
supposed to be
(T) -> K
?
And what's the function supposed to do? Can you give some example inputs and expected output?
h
Hey, sorry I was whisked away. You are absolutely right with the
(T) -> K
. Now I can't remember what I exactly wanted to achieve in the first place and I am now at
Copy code
fun <T, K> List<T>.intersectBy2(new: List<T>, keySelector: (T) -> K): List<K> =
    map { keySelector(it) }
    .intersect(new.map { keySelector(it) }.toSet())
    .toList()
I just wanted a generic way to tell me the changed... OH now I remember lol. I didn't just want to get the IDs of the changed entries. That was the reason I put it in a Pair. So
changedBy()
might be more appropriate. I should have been more verbose in my question and not give in to Meeting pressure or just do it after. Quick correction:
Copy code
fun <T, K> List<T>.changedBy(new: List<T>, keySelector: (T) -> K): List<K> {
    val old = this.associateBy { keySelector(it) }
    val newed = new.associateBy { keySelector(it) }
    val changed = old.filter { (key, value) -> newed[key]?.let { it != value } == true }
    val result = changed.keys.toList()
    return result
}
edit: and updated:
Copy code
fun <T, K> List<T>.changedBy(new: List<T>, keySelector: (T) -> K): List<K> =
    associateBy { keySelector(it) }
    .let { old -> new
        .associateBy { keySelector(it) }
        .filter { (key, value) -> old[key] == value }
        .keys
        .toList()
    }
d
In your last example, should that be
old[key] != value
? Looks like your selecting the keys where the values are the same between the two lists.
I think this will do what you want, and probably faster too.
Copy code
fun <T, K> List<T>.changedBy(new: List<T>, keySelector: (T) -> K): List<K> =
    (new - this.toSet()).map(keySelector)
Since this uses
toSet()
, it assumes hashCode is implemented correctly.
h
Hey, yes like you said it's
old[key] != value
. I tried your solution, but it also carries over new values that have not been in the list before. As an example from these two lists (assume Pairs)
[(1, "old1"), (2, "old2"), (3, "old3")]
and
[(2, "old2"), (3, "new3"), (4, "new4")]
The call
old.changedBy(new) { it.first }
should only output
[3]
, your's gives
[3, 4]
.
d
Ah, didn’t realize you only wanted old values.