按多个属性并使用自然排序对对象列表进行排序



我想按布尔属性和字符串属性对对象列表进行排序。

字符串应该不区分大小写地进行比较,并使用此处定义的自然排序顺序

一个例子可以是按活着然后按名字对人进行排序:

class Person(
public val name: String,
public val alive : Boolean

)

我希望名称排序看起来像这样:

  • "1亚当">
  • "13亚当">
  • "20亚当">
  • "100亚当">
  • "亚当0〃
  • "Adam 1〃
  • "亚当10〃
  • "Adam 20〃
  • "Adam 100〃
  • "Brian">
  • "乔治

我曾尝试过使用naturalOrder Comparable对字符串列表进行排序,尽管它似乎区分大小写,但它看起来确实很有前景:

val personsStr = setOf("1 Adam", "21 Adam", "13 Adam", "Adam 1", "adam 1", "Adam 0", "Adam 20", "Adam 11", "Bryan", "George")
val personStrSorted = personsStr.sortedWith(naturalOrder())

我要找的是这样的东西:

persons.sortedWith( compareBy<Person>{ it.alive }.thenBy{ it.name.toLowerCase() /*and done using natural sorting*/ } )

问题:通过哪种方式可以实现所需的排序?

一些技巧性的解决方案:按alive将集合拆分为2个集合,并按name对这两个集合进行排序(这会产生自然顺序(::

val personsStr = setOf( Person("1 Adam", true ), Person("21 Adam", false), Person("13 Adam", true), Person( "Bryan", true) )   
val personsAlive = personsStr.filter{it.alive}.sortedBy{it.name}
val personsNotAlive = personsStr.filter{!it.alive}.sortedBy{it.name}

然后你可以合并它们:

val result = setOf(personsAlive, personsNotAlive)

我相信您必须实现您的";自然排序";手动。

";自然秩序;在Kotlin中,简单地定义为当没有提供特殊的比较器(文档链接(时,Comparable<T>实现的排序顺序:

首先,存在自然顺序。它是为Comparable接口的继承者定义的。当没有指定其他顺序时,使用自然顺序对它们进行排序。

使用naturalOrder()应该为Comparable的任何实现者提供由compareTo运算符定义的常规顺序。这意味着,对于字符串,自然顺序实际上是字母顺序

如果您知道您的名称总是采用<name><num> <name><name> <num>的形式,那么您应该能够通过实际解析数字来实现比较器。

一种方法可以是这样的:

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

你可以在那里运行:https://pl.kotl.in/u9O2wBFgk

或者,您可以首先用ComplexName属性定义Person,以避免每次比较时都计算它。

请注意,从技术上讲,您可以通过更高级的解析实现具有自然排序的比较器,从而将该方法推广到任何位置的数字。不过,处理单词中的数字会更棘手,但这取决于你的目标。

最新更新