I have array with multiple objects with the same key, Other objects have empty values and I was hoping to use distinctBy to remove duplicates and get objects which values has the longest string.
data class ObjA(val key: String, val value: String)
fun test() {
val listOfA = mutableListOf(
ObjA("one", ""), //This will be taken in distinctBy.
ObjA("one", "o"),
ObjA("one", "on"),
ObjA("one", "one"), //This will be ignored in distinctBy. I WANT THIS ONE.
ObjA("two", ""), //This will be returned in distinctBy
ObjA("two", "2"),
ObjA("two", "two"), //But I want this.
ObjA("three", "3"),
ObjA("four", "4"),
ObjA("five", "five")
)
val listOfAWithNoDuplicates = listOfA.distinctBy { it.key }
for (o in listOfAWithNoDuplicates) {
println("key: ${o.key}, value: ${o.value}")
}
}
Output
key: one, value:
key: two, value:
key: three, value: 3
key: four, value: 4
key: five, value: five
How to make this work. Any help will be appreciated.
Returns a list containing only elements from the given array having distinct keys returned by the given selector function. Among elements of the given array with equal keys, only the first one will be present in the resulting list.
As distinctBy
just returns the distinct keys based on your selector (and in the order of the list), you end up with unique keys, but not yet with the values you want.
For that particular use-case I would probably just sort beforehand, followed by the distinctBy
listOfA.sortedByDescending { it.value.length }
.distinctBy { it.key }
Which creates a new list at sortedByDescending
or you just sort the current list beforehand (sortByDescending
) and apply distinctBy
later on, e.g.:
listOfA.sortByDescending { it.value.length }
listOfA.distinctBy { it.key }
In both cases you get a new List<ObjA>
with the expected values.
Several other variants come to my mind as well. All those variants will put the results into a Map<String, ObjA>
where the key is actually the unique ObjA.key
. You may want to call .values
directly if you are not interested in the key/ObjA
-mapping.
variant using groupingBy
and reduce
:
listOfA.groupingBy { it.key }
.reduce { _, acc, e->
maxOf(e, acc, compareBy { it.value.length })
}
variant using a plain forEach
/for
and filling its own Map
:
val map = mutableMapOf<String, ObjA>()
listOfA.forEach {
map.merge(it.key, it) { t, u ->
maxOf(t, u, compareBy { it.value.length })
}
}
variant using fold
and merge
(very similar to the previous... just using fold
instead of for
/forEach
):
listOfA.fold(mutableMapOf<String, ObjA>()) { acc, objA ->
acc.apply {
merge(objA.key, objA) { t, u ->
maxOf(t, u, compareBy { it.value.length })
}
}
}
variant using groupBy
followed by mapValues
(but you are then actually creating 1 map which you discard immediately):
listOfA.groupBy { it.key } // the map created at this step is discarded and all the values are remapped in the next step
.mapValues { (_, values) ->
values.maxBy { it.value.length }!!
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With