jwass
02/03/2022, 2:03 PMdata class Thing(val x: Int, val y: Int, val ignoreMe: String)
which I want to put in a set to perform set operations, but ignore certain fields for the purpose of set comparison. i.e. only considering a subset of fields for equality comparisons.
val x = setOf(Thing(5, 5, "Hello"), Thing(10, 6, "Goodbye"), Thing(100, 3, "Zzzz"))
val y = setOf(Thing(5, 5, "Baz"), Thing(10, 6, "Goodbye"))
val result = x.minus(y)
-> result = setOf(Thing(100, 3, "Zzzz")) // because Thing(5, 5, "Hello") is considered equal to Thing(5, 5, "Baz")
In reality the data classes are a bit richer, about 6 fields.
I thought I could build a sorted set with a custom comparator, but that requires implementing a sorting on the data classes when I only really want a custom equality for that set.
I only want this custom equality behaviour in one context, so it’s not appropriate to actually re-implement equals
etc on the data classes directly.
Any ideas about if there’s a simple way to achieve this?
(edit - found a nice idea on stack overflow - just wrap the objects in a special purpose class)Joffrey
02/03/2022, 2:07 PMdistinctBy { ... }
may be sufficient. Otherwise you can also use a regular HashSet
but convert your items into some other data class containing a subset of the fieldsjwass
02/03/2022, 2:09 PMJoffrey
02/03/2022, 2:10 PMsubtractBy
🙂jwass
02/03/2022, 2:10 PMjwass
02/03/2022, 2:10 PMselector
on this page is the place I think I’d find it. https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/jwass
02/03/2022, 2:11 PMJoffrey
02/03/2022, 2:12 PMsubtractBy
based on this idea btw, maybe you can find inspiration in the implementation of distinctBy
jwass
02/03/2022, 2:15 PMjwass
02/03/2022, 2:16 PMJoffrey
02/03/2022, 2:17 PMdistinctBy
by just initializing the hashset with the elements of the second set, couldn't you?Joffrey
02/03/2022, 2:18 PMpublic inline fun <T, K> Iterable<T>.distinctBy(selector: (T) -> K): List<T> {
val set = HashSet<K>()
val list = ArrayList<T>()
for (e in this) {
val key = selector(e)
if (set.add(key))
list.add(e)
}
return list
}
Joffrey
02/03/2022, 2:19 PMsubtractBy
, pass another set as first argument, and initialize the first hashset with elements of the set passed as argument, it seems it would do the trick.
(and you'll probably want to replace ArrayList
with a HashSet
too if you want to keep it a set)jwass
02/03/2022, 2:22 PMpublic inline fun <T, K> Iterable<T>.subtractBy(selector: (T) -> K, other: Iterable<T>): List<T> {
val set = HashSet<K>()
val list = ArrayList<T>()
for (e in this) {
val key = selector(e)
if (set.add(key))
list.add(e)
}
for (e in other){
val key = selector(e)
if (set.contains(key)) {
set.remove(e)
}
}
return list
}
jwass
02/03/2022, 2:22 PMJoffrey
02/03/2022, 2:24 PMinline fun <T, K> Iterable<T>.subtractBy(other: Set<T>, selector: (T) -> K): List<T> {
val visited = other.mapTo(HashSet(), selector)
val result = ArrayList<T>()
for (e in this) {
val key = selector(e)
if (visited.add(key))
result.add(e)
}
return result
}
jwass
02/03/2022, 2:24 PMselector
result. So you need to store a hash table of selected value to original.jwass
02/03/2022, 2:25 PMjwass
02/03/2022, 2:25 PMJoffrey
02/03/2022, 2:25 PMjwass
02/03/2022, 6:52 PM