概要
ご存知の方も多いかと思いますが、
以下のように、数値ソートと文字列ソートとでは、並び替えの規則が異なります。
fun main() {
// 数値ソート
listOf(100, 99, 11, 10, -1, -100, -10, 9, 3, 1)
.sorted().also(::println)
// [-100, -10, -1, 1, 3, 9, 10, 11, 99, 100]
// 文字列ソート
listOf("100", "99", "A2", "11", "A10", "10", "B1", "-1", "-100", "-10", "9", "3", "1")
.sorted().also(::println)
// [-1, -10, -100, 1, 10, 100, 11, 3, 9, 99, A10, A2, B1]
}
本投稿は、文字列のリストであっても、数値の集合に関しては数値ソートライクに並び替えたいな、というものです。
// 数値ソート
// [-100, -10, -1, 1, 3, 9, 10, 11, 99, 100]
// 文字列ソート
// [-1, -10, -100, 1, 10, 100, 11, 3, 9, 99, A10, A2, B1]
// 今回やりたいソート
// [-100, -10, -1, 1, 3, 9, 10, 11, 99, 100, A10, A2, B1]
割と無視できない問題点として、今回用意したコードはIntしか考慮していないために、例えば小数などに関しては従来通り文字列ソートされてしまいます。
簡易版としてご了承ください。
実装コード
/**
* Int風ソートComparator
*
* 1. Int変換可能なもの間ではIntとして比較
* 2. Int変換不可能なもの間ではStringとして比較
* 3. Int変換可能なものと不可能なものではInt変換可能なものを優先
*
* 例: listOf("100", "99", "A2", "11", "A10", "10", "B1", "-1", "-100", "-10", "9", "3", "1").sortedWith(compareAsIntStyle{ it })
* -> [-100, -10, -1, 1, 3, 9, 10, 11, 99, 100, A10, A2, B1]
*/
inline fun <T : Any?> compareAsIntStyle(crossinline selector: (T) -> String): Comparator<T> =
Comparator { a, b ->
val t1 = selector(a)
val t2 = selector(b)
val t1Int = t1.toIntOrNull()
val t2Int = t2.toIntOrNull()
when {
t1Int != null && t2Int != null -> t1Int.compareTo(t2Int)
t1Int == null && t2Int == null -> t1.compareTo(t2)
t1Int != null -> -1
else -> 1
}
}
テストコード
fun main() {
listOf("100", "99", "A2", "11", "A10", "10", "B1", "-1", "-100", "-10", "9", "3", "1")
.sortedWith(compareAsIntStyle{ it }).also(::println)
// [-100, -10, -1, 1, 3, 9, 10, 11, 99, 100, A10, A2, B1]
listOf("100", "99", "A2", "11", "A10", "10", "B1", "-1", "-100", "-10", "9", "3", "1").map(::StringData)
.sortedWith(compareAsIntStyle { it.value }).also(::println)
// [StringData(value=-100), StringData(value=-10), StringData(value=-1), StringData(value=1), StringData(value=3), StringData(value=9), StringData(value=10), StringData(value=11), StringData(value=99), StringData(value=100), StringData(value=A10), StringData(value=A2), StringData(value=B1)]
}
/** テスト用Stringデータクラス */
private data class StringData(val value: String)
最後に
車輪の再発明感がものすごいですが、探した範囲ではいい感じのものを見つけることができませんでした。
絶対もっといいものがあると思いますが...
改良案/代替案等ありましたら、どうぞよろしくおねがいします。