CallMePedro
CallMePedro

Reputation: 91

Sorting in Kotlin

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

Answers (4)

Nikolai  Shevchenko
Nikolai Shevchenko

Reputation: 7521

Just

myList?.sortedWith(compareBy({ it.name }, { extractInt(it.id) }))

Upvotes: 2

Housefly
Housefly

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

Alexey Romanov
Alexey Romanov

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

JArgente
JArgente

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

Related Questions