使用 Kotlin 对包含数字的字符串进行排序

5

我想对一些包含数字的字符串进行排序,但排序后结果变成了这样:["s1", "s10", "s11", ... ,"s2", "s21", "s22"]。在搜索时,我发现有一个与之相同的问题(链接)。但是在我的例子中,我有一个mutableList<myModel>,我必须将所有的字符串myModel.title放入可变列表中,并放置在以下代码下方:

   val sortData = reversedData.sortedBy {
          //pattern.matcher(it.title).matches()
             Collections.sort(it.title, object : Comparator<String> {
                override fun compare(o1: String, o2: String): Int {
                    return extractInt(o1) - extractInt(o2)
                }

                 fun extractInt(s: String): Int {
                     val num = s.replace("\\D".toRegex(), "")
                     // return 0 if no digits found
                     return if (num.isEmpty()) 0 else Integer.parseInt(num)
                 }
            })
        }

我在使用.sortedByCollections.sort(it.title)时遇到了错误,请帮我修复。


我有一个错误:您能透露是哪一个吗? - Henry
首先,我必须找到一种将it.title转换为可变列表的方法。因为Collections.sort需要它。我不知道如何将模型中的所有it.title推入可变列表中,然后我认为我必须在.sortedBy修复之前使用return。 - Mehrdad Dolatkhah
不要混淆 Collections.sortsortedBy。你可能想看一下 sortWith。此外,sortedBysortedWith 是类似的(它们返回一个新的列表),而 sortWith 则操作于当前列表。 - Roland
被接受的答案如何成为解决你问题的唯一答案?它比其他任何答案都要复杂得多,它使用了Observable/Subscriber,这是你没有使用过的东西... - Roland
5个回答

8
你可以使用sortWith代替sortBy,例如:
class Test(val title:String) {
  override fun toString(): String {
    return "$title"
  }
}

val list = listOf<Test>(Test("s1"), Test("s101"),
Test("s131"), Test("s321"), Test("s23"), Test("s21"), Test("s22"))
val sortData = list.sortedWith( object : Comparator<Test> {
override fun compare(o1: Test, o2: Test): Int {
    return extractInt(o1) - extractInt(o2)
}

fun extractInt(s: Test): Int {
    val num = s.title.replace("\\D".toRegex(), "")
    // return 0 if no digits found
    return if (num.isEmpty()) 0 else Integer.parseInt(num)
}

})

将会输出: [s1, s21, s22, s23, s101, s131, s321]


我必须将所有的 it.title 推入一个列表中并在 sortData 中使用吗? - Mehrdad Dolatkhah
@MehrdadDolatkhah 我更新了我的示例,希望它能给你解决问题的思路,以及如何访问标题(属性)。将类Test更改为你的MyModel类。 - hakim

4
基于您发布的数据,可能的解决方案如下:
sortedBy { "s(\\d+)".toRegex().matchEntire(it)?.groups?.get(1)?.value?.toInt() }

当然,我会将正则表达式移出lambda函数,但这种方式更加简洁。

2

您提到需要一个 MutableList,但目前还没有,您可以使用 sortedBy 或者 sortedWith(如果您需要使用比较器),这些方法会返回您当前列表的一个新排序后的列表,例如:

val yourMutableSortedList = reversedData.sortedBy {
  pattern.find(it)?.value?.toInt() ?: 0
}.toMutableList() // now calling toMutableList only because you said you require one... so why don't just sorting it into a new list and returning a mutable list afterwards?

您可以利用 compareBy(或Java的Comparator.comparing)来进行sortedWith排序。

如果您只想对现有的可变列表进行排序,请使用sortWith(或Collections.sort):

reversedData.sortWith(compareBy {
  pattern.find(it)?.value?.toInt() ?: 0
})

// or using Java imports:
Collections.sort(reversedData, Compatarator.comparingInt {
  pattern.find(it)?.value?.toInt() ?: 0 // what would be the default for non-matching ones?
})

当然,您也可以尝试使用其他比较器助手(例如混合空值在最后等),例如:

reversedData.sortWith(nullsLast(compareBy {
  pattern.find(it)?.value
}))

对于以上示例,我使用了以下Regex

val pattern = """\d+""".toRegex()

2
可能的解决方案如下:
  reversedData.toObservable()
                    .sorted { o1, o2 ->
                        val pattern = Pattern.compile("\\d+")
                        val matcher = pattern.matcher(o1.title)
                        val matcher2 = pattern.matcher(o2.title)

                        if (matcher.find()) {
                            matcher2.find()
                            val o1Num = matcher.group(0).toInt()
                            val o2Num = matcher2.group(0).toInt()

                            return@sorted o1Num - o2Num
                        } else {
                            return@sorted o1.title?.compareTo(o2.title ?: "") ?: 0
                        }
                    }
                    .toList()
                    .subscribeBy(
                        onError = {
                            it
                        },
                        onSuccess = {
                            reversedData = it
                        }
                    )

使用 Kotlin 的正则表达式扩展,它们更短、更简单。查看我的答案以获取示例。 - m0skit0

-1

我为我的JSON排序编写了一个自定义比较器。它可以从裸的字符串/数字/空值进行调整。

fun getComparator(sortBy: String, desc: Boolean = false): Comparator<SearchResource.SearchResult> {
    return Comparator { o1, o2 ->
        val v1 = getCompValue(o1, sortBy)
        val v2 = getCompValue(o2, sortBy)

        (if (v1 is Float && v2 is Float) {
            v1 - v2
        } else if (v1 is String && v2 is String) {
            v1.compareTo(v2).toFloat()
        } else {
            getCompDefault(v1) - getCompDefault(v2)
        }).sign.toInt() * (if (desc) -1 else 1)
    }
}

private fun getCompValue(o: SearchResource.SearchResult, sortBy: String): Any? {
    val sorter = gson.fromJson<JsonObject>(gson.toJson(o))[sortBy]
    try {
        return sorter.asFloat
    } catch (e: ClassCastException) {
        try {
            return sorter.asString
        } catch (e: ClassCastException) {
            return null
        }
    }
}

private fun getCompDefault(v: Any?): Float {
    return if (v is Float) v else if (v is String) Float.POSITIVE_INFINITY else Float.NEGATIVE_INFINITY
}

请调整您的比较器并分享适应版本。 - Yazon2006

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接