Home > OS >  Sort by multiple criteria with Kotlin
Sort by multiple criteria with Kotlin

Time:02-15

We want to sort objects by three different criteria, the criteria with higher priority overrides the next one):

  1. status field
  2. time field
  3. sort field
  • status are enums and have a custom sorting, which we implemented by a comparator. (unknown, open, closed,)
  • sortBy Field is an Integer and can be sorted accordingly
  • time field can be null, we only want to sort the ones with null to the top and leave the remaining sorting as it is

example:

  • Object1(status: open, time: 1, sort: 4)
  • Object2(status: closed, time: null, sort: 1)
  • Object3(status: unknown, time: 2, sort: 3)
  • Object4(status: unknown, time: null, sort: 2)
  • Object5(status: open, time: 1, sort: 5)

sorted:

  1. Object 3 (status: unknown)
  2. Object 4 (status: unknown, next item in the original list)
  3. Object 2 (status != unknown, time: null)
  4. Object 1 (status != unknown, time != null, sort: 4)
  5. Object 5 (status != unknown, time != null, sort: 5)

we return the list the following way:

list.sortedWith(statusComparator.thenBy { it.sort }

What is missing is the sorting of items with time = null. How do I sort the items with time = null to the top and leave the remaining sorting untouched?

CodePudding user response:

You can combine Comparator objects with thenComparing so you probably want to write a comparator for the time something like

val timeComparator = Comparator<YourObject> { first, second ->
    when {
        first.time == null && second.time != null -> -1
        first.time == null && second.time == null -> 0
        first.time != null && second.time == null -> 1
        else -> 0
    }
}

And then change

list.sortedWith(statusComparator.thenBy { it.sort }

to

list.sortedWith(statusComparator.thenComparing(timeComparator).thenBy { it.sort })

CodePudding user response:

To sort depending if the value is null we can... do exactly this, sort by checking if it is null:

list.sortedWith(
    statusComparator
        .thenBy { it.time != null }
        .thenBy { it.sort }
)

It works, because false is considered smaller than true. And it returns true for any non-null value, so all non-null values are considered the same.

We can join time and sort steps into a single one, by returning null if time is null and sort otherwise:

.thenBy { item -> item.time?.let { item.sort } }
// or:
.thenBy { if (it.time == null) null else it.sort }

It could be a little more performant, but I consider this harder to understand and less flexible.

  • Related