Haywirehax
Haywirehax

Reputation: 13

Kotlin filter nested lists

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.

Upvotes: 0

Views: 238

Answers (2)

broot
broot

Reputation: 28322

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
    }
}

Upvotes: 1

Haywirehax
Haywirehax

Reputation: 13

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

Upvotes: 0

Related Questions