I want to filter a list that has episodes of series. The list looks a bit like this:
[
{
"media": {
"episode_number": "7"
},
"collection": {
"collection_id": "26464"
}
},
{
"media": {
"episode_number": "6"
},
"collection": {
"collection_id": "26464"
}
},
{
"media": {
"episode_number": "9"
},
"collection": {
"collection_id": "123456"
}
}
]
I only want the highest episode number of each collection (serie). so the result would be:
[
{
"media": {
"episode_number": "7"
},
"collection": {
"collection_id": "26464"
}
},
{
"media": {
"episode_number": "9"
},
"collection": {
"collection_id": "123456"
}
}
]
I tried this using a filter but I can't figure out how to use the nested structure.
CodePudding user response:
What you need here is to group items by collection.collectionId
and then for each group look for an item with maximum value of media.episode_number
. In Kotlin it can be done almost as it was said above:
items
.groupBy { it.collection.collectionId }
.values
.map { group -> group.maxByOrNull { it.media.episode_number }!! }
Above solution creates intermediary collections that are not strictly necessary. We can improve the performance by calculating the max while grouping. Unfortunately, Kotlin does not provide a handy maxBy()
function in this case, but we can create it by ourselves:
fun main() {
val items = ...
items
.groupingBy { it.collection.collectionId }
.maxBy { _, it -> it.media.episode_number }
.values
}
fun <T, K, R : Comparable<R>> Grouping<T, K>.maxBy(selector: (K, T) -> R): Map<K, T> {
return reduce { k, it1, it2 ->
if (selector(k, it1) >= selector(k, it2)) it1 else it2
}
}
CodePudding user response:
I found the solution:
list = list.sortedBy { it.collection.collectionId }.filter { item ->
item == list.first { it.collection.collectionId == item.collection.collectionId }
}
(turns out they are already sorted by episode in the correct order)
I think this is one of the fastest solutions, but feel free to share more
CodePudding user response:
A general solution that will work with a list of that type with any initial sorting, would be this:
list
.groupBy { episode -> episode.collection.collectionId }
.map { (_, episodeList) ->
episodeList.maxByOrNull { episode -> episode.media.episodeNumber }
}
.filterNotNull()
This will create a map where the key is the collectionId
and the value is a list of all the episodes with that collectionId
. Then for each entry of the map it will select the episode with the highest episodeNumber
.