I believe you will have to implement your notion of "natural ordering" manually.
The "natural order" in Kotlin is simply defined as the order by which Comparable<T> implementations are sorted when no special comparator is provided (doc link):
First, there is natural order. It is defined for inheritors of the Comparable interface. Natural order is used for sorting them when no other order is specified.
Using naturalOrder() should give you the usual order defined by the compareTo operator for any implementor of Comparable. This means that for string, the natural order is in fact the alphabetical order.
If you know that your names are always in the form <name>, <num> <name> or <name> <num>, you should be able to implement a comparator by actually parsing the number.
One way to do it could be something like this:
fun main() {
val personsStr = setOf("1 Adam", "21 Adam", "100 Adam", "Adam 1", "adam 1",
"Adam 3", "Adam 20", "Adam 11", "Bryan", "George", "bob" )
val persons = personsStr.map { Person(name = it, alive = kotlin.random.Random.nextBoolean()) }
val personsSorted = persons.sortedWith(compareBy({ it.alive }, { it.name.parseName() }))
println(personsSorted.joinToString("\n"))
}
data class Person(
val name: String,
val alive: Boolean
)
data class ComplexName(
val leadingNum: Int?,
val shortName: String,
val trailingNum: Int?
) : Comparable<ComplexName> {
override operator fun compareTo(other: ComplexName) = compareValuesBy(this, other, comparator, { it })
companion object {
private val comparator = compareBy<ComplexName>(
{ it.leadingNum ?: Int.MAX_VALUE }, // no leading number is behind those with leading number
{ it.shortName.toLowerCase() },
{ it.trailingNum },
)
}
}
fun String.parseName(): ComplexName {
val parts = split(" ")
return when (parts.size) {
1 -> ComplexName(null, this, null)
2 -> {
val (first, second) = parts
val firstNum = first.toIntOrNull()
when (firstNum) {
null -> ComplexName(null, first, second.toInt())
else -> ComplexName(firstNum, second, null)
}
}
else -> throw IllegalArgumentException("unsupported name format: $this")
}
}
You can run this there: https://pl.kotl.in/u9O2wBFgk
Alternatively, you could define Person with a ComplexName property in the first place to avoid computing it each time when comparing.
Note that you can technically implement a comparator with natural ordering with fancier parsing, generalizing the approach for numbers in any position. It would be trickier to deal with numbers inside words though, but that depends on your goal.