1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

[kotlinメモ]文字列リストをできるだけ数値ソートライクに並び替えたい

Last updated at Posted at 2023-01-04

概要

ご存知の方も多いかと思いますが、
以下のように、数値ソートと文字列ソートとでは、並び替えの規則が異なります。

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)

最後に

車輪の再発明感がものすごいですが、探した範囲ではいい感じのものを見つけることができませんでした。
絶対もっといいものがあると思いますが...

改良案/代替案等ありましたら、どうぞよろしくおねがいします。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?