Reputation: 91
I have a problem with sorting objects in Kotlin. I have class
Home(id : String, name : String)
and I would like to sort it first by name and then by ids, where ids can be:
This solution does not provide the correct result. Please not that ids are String so the result I'm getting is -> 1, 10, 2, 3
myList = myList?.sortedWith(compareBy<Home> { it.name }.thenBy { it.id })
How to add in then.by
correct comparator, or how it should be sorted?
Greetings
@EDIT
I found a solution, but how to add it in thenBy
?
Collections.sort(strings, 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)
}
})
Upvotes: 2
Views: 752
Reputation: 7521
Just
myList?.sortedWith(compareBy({ it.name }, { extractInt(it.id) }))
Upvotes: 2
Reputation: 4422
data class Person(
val name: String,
var id: String
)
fun sortPersonsWithParsedIds() {
val p1 = Person("abc", "2")
val p2 = Person("abc", "1")
val p3 = Person("xyz", "10kafsd")
val p4 = Person("xyz", "1asda")
val p5 = Person("pqr", "2aaa")
val p6 = Person("pqr", "20")
val p7 = Person("pqr", "20aa")
val list = listOf(p1, p2, p3, p4, p5, p6, p7)
val sortedList = list.sortedWith(compareBy({ it.name }, { v -> extractInt(v.id) }))
sortedList.forEach { println(it) }
}
private 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)
}
Result:
Person(name=abc, id=1)
Person(name=abc, id=2)
Person(name=pqr, id=2aaa)
Person(name=pqr, id=20)
Person(name=pqr, id=20aa)
Person(name=xyz, id=1asda)
Person(name=xyz, id=10kafsd)
Upvotes: 2
Reputation: 170723
It would be .thenBy { extractInt(it.id) }
(where you declare extractInt
separately). Or just put the definition of extractInt
there if that's the only place you need it:
compareBy<Home> { it.name }.thenBy {
val num = it.id.replace("\\D".toRegex(), "")
// return 0 if no digits found
if (num.isEmpty()) 0 else Integer.parseInt(num)
}
Of course, these solutions will consider e.g. 10
and 10a
or as
and 0
to be equal; is that really what you want? If not, you can return a pair of the extracted integer part and the remaining string.
Upvotes: 1
Reputation: 2297
You can extend the comparable interface using an object declaration and put there the logic to compare both strings, this way you can use it in the thenBy clause:
val myList= myList?.sortedWith(compareBy<Home> { it.name }.thenBy {
object: Comparable<String>{
override fun compareTo(other: String): Int {
return extractInt(it.id) - extractInt(other)
}
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)
}
}
})
Upvotes: 1