如何使用 Kotlin 可用的便捷方法在末尾按多个值和空值对 collection 进行排序
How to sort a collection by multiple values AND nulls at the end using the available convenient methods of Kotlin
我以前使用过sortedWith和compareBy方法,它能够使用多个参数进行排序。
我之前也用过nullsLast方法,和其他两个一样方便。
但是,使用这些方法,我想不出一种方法来使用以下排序规则对 collection 进行排序:
鉴于 Class:
data class MyClass(
val varA: String?,
val varB: String
)
按照varA字母顺序排序,nulls/blank排在最后;然后
按照 varB 字母顺序排序
假设我有以下 collection:
val collection = listOf(
MyClass(null, "A"),
MyClass("a", "B"),
MyClass("c", "C"),
MyClass("b", "D"),
MyClass("", "E"),
MyClass("a", "F"),
MyClass("b", "G"),
MyClass(null, "H"),
MyClass("", "I")
)
它应该被排序为:
MyClass("a", "B")
MyClass("a", "F")
MyClass("b", "D")
MyClass("b", "G")
MyClass("c", "C")
MyClass(null, "A")
MyClass("", "E")
MyClass(null, "H")
MyClass("", "I")
有没有我可以使用的 one-liner 代码,就像使用 vararg 参数
的 compareBy 一样
当然有一条线:
collection.sortedBy {if(it.varA != null && it.varA.isNotEmpty()) it.varA + it.varB else "z" + it.varB}
然后打印出来:
output.forEach { println("Myclass(${it.varA}, ${it.varB})") }
输出:
Myclass(a, B)
Myclass(a, F)
Myclass(b, D)
Myclass(b, G)
Myclass(c, C)
Myclass(null, A)
Myclass(, E)
Myclass(null, H)
Myclass(, I)
解释:
我们需要在按 varA
排序和按 varB
排序时拆分案例。这就是为什么有一个条件 if(it.varA != null && it.varA.isNotEmpty())
,它表示只有当它的值不是 null
或 ""
.
时,我们才会按 varA
排序
否则,我们按字符串"z" + it.varB
排序,这意味着我们按varB
排序,但带有z
前缀,这将确保这些项目位于已排序集合的末尾。
为了更容易理解它是如何工作的,您可以试试下面的代码:
val output = collection.map { if(it.varA != null && it.varA.isNotEmpty()) it.varA + it.varB else "z" + it.varB }
output.sortedBy { it }.forEach { println(it) }
这个将输出这些字符串:
aB
aF
bD
bG
cC
zA
zE
zH
zI
现在应该更明显了。 :-)
我会这样做(参见 kotlin.comparisons
documentation):
val comparator = compareBy<String, MyClass>(nullsLast(), { it.varA.let { if (it == "") null else it } }).thenBy { it.varB }
collection.sortedWith(comparator)
我以前使用过sortedWith和compareBy方法,它能够使用多个参数进行排序。
我之前也用过nullsLast方法,和其他两个一样方便。
但是,使用这些方法,我想不出一种方法来使用以下排序规则对 collection 进行排序:
鉴于 Class:
data class MyClass(
val varA: String?,
val varB: String
)
按照varA字母顺序排序,nulls/blank排在最后;然后
按照 varB 字母顺序排序
假设我有以下 collection:
val collection = listOf(
MyClass(null, "A"),
MyClass("a", "B"),
MyClass("c", "C"),
MyClass("b", "D"),
MyClass("", "E"),
MyClass("a", "F"),
MyClass("b", "G"),
MyClass(null, "H"),
MyClass("", "I")
)
它应该被排序为:
MyClass("a", "B")
MyClass("a", "F")
MyClass("b", "D")
MyClass("b", "G")
MyClass("c", "C")
MyClass(null, "A")
MyClass("", "E")
MyClass(null, "H")
MyClass("", "I")
有没有我可以使用的 one-liner 代码,就像使用 vararg 参数
的 compareBy 一样当然有一条线:
collection.sortedBy {if(it.varA != null && it.varA.isNotEmpty()) it.varA + it.varB else "z" + it.varB}
然后打印出来:
output.forEach { println("Myclass(${it.varA}, ${it.varB})") }
输出:
Myclass(a, B)
Myclass(a, F)
Myclass(b, D)
Myclass(b, G)
Myclass(c, C)
Myclass(null, A)
Myclass(, E)
Myclass(null, H)
Myclass(, I)
解释:
我们需要在按 varA
排序和按 varB
排序时拆分案例。这就是为什么有一个条件 if(it.varA != null && it.varA.isNotEmpty())
,它表示只有当它的值不是 null
或 ""
.
varA
排序
否则,我们按字符串"z" + it.varB
排序,这意味着我们按varB
排序,但带有z
前缀,这将确保这些项目位于已排序集合的末尾。
为了更容易理解它是如何工作的,您可以试试下面的代码:
val output = collection.map { if(it.varA != null && it.varA.isNotEmpty()) it.varA + it.varB else "z" + it.varB }
output.sortedBy { it }.forEach { println(it) }
这个将输出这些字符串:
aB
aF
bD
bG
cC
zA
zE
zH
zI
现在应该更明显了。 :-)
我会这样做(参见 kotlin.comparisons
documentation):
val comparator = compareBy<String, MyClass>(nullsLast(), { it.varA.let { if (it == "") null else it } }).thenBy { it.varB }
collection.sortedWith(comparator)