Bob Redity
Bob Redity

Reputation: 621

How can I filter array of <Any> by its type -Kotlin?

I have an array arrayOf<Any>("Apple",46,"287",574,"Peach","3","69",78,"Grape","423") and I need to sort it: firstly numbers suppose to go like "3",46,"69"... and then words by alphabet... So I am trying first to divide them on separate arrays by type and then some manipulation. Do you have advice of how to solve this problem?

Upvotes: 0

Views: 315

Answers (4)

gidds
gidds

Reputation: 18557

Here's a concise* approach:

val a = arrayOf<Any>("Apple", 46, "287", 574, "Peach", "3", "69", 78, "Grape", "423")

val (numbers, nonNumbers) = a.partition{ it.toString().toIntOrNull() != null }
val result = (numbers.sortedBy{ it.toString().toInt() }
            + nonNumbers.sortedBy{ it.toString() })

println(result) // [3, 46, 69, 78, 287, 423, 574, Apple, Grape, Peach]

This uses partition() to split the array into two lists: one holding all the numbers, and one holding the rest. (To handle all possible types, it calls toString(), and then determines whether that string would be a valid integer.)

Then it sorts the first sublist (holding numbers) according to their integer value, and the second sublist lexicographically; finally, it uses + to join the sorted sublists into one.

This will work for elements of all possible types, not just String and Int. (However, although it's not clear from the output, the result preserves the original types; the string and integer conversions are only done temporarily for the purposes of sorting.)

Note that it produces a list, not an array. (It will also operate on a list or other collection.) Lists are much better supported in the standard library, can be resized, have a decent toString() implementation, come in mutable and immutable versions, have countless implementations with different characteristics, work better with generics, and several other advantages. Arrays are mainly for interoperability with old code, and a few specific uses (varargs, low-level implementation, and the main() method); for everything else, lists are much preferable.

(However, if you really need an array result, you can call result.toTypedArray().)


* While it's fun to puzzle out the shortest solution, that doesn't necessarily make it the best…

In practice, the best code is clear, simple, easy to read and maintain; conciseness can help that, or it can go too far, and this answer might go slightly too far. (In fact, I've tweaked it since first posted, naming the sublists in the hope of making it a little more readable.)

This is also less efficient than some other answers, as it creates lots of temporary objects (four lists and many strings). In many cases, that may not matter — but it's worth being aware of.

In any event, it's always worth being aware of alternatives, and so I hope this is instructive!

Upvotes: 3

Muhammad Rio
Muhammad Rio

Reputation: 939

You can try my solution


val a = arrayOf<Any>("Apple",46,"287",574,"Peach","3","69",78,"Grape","423")
val numbers = mutableListOf<Any>()
val strings = mutableListOf<String>()

for(e in a){
    if(e is Int) numbers.add(e)
    if(e is String){
        if(e.toIntOrNull() == null){
            strings.add(e)
        } else {
            numbers.add(e)
        }
    }
}
   
val result = numbers.also { value -> value.sortBy{it.toString().toInt()} } + strings.sorted()
   
println(result) // [3, 46, 69, 78, 287, 423, 574, Apple, Grape, Peach]

Upvotes: 1

k314159
k314159

Reputation: 11080

You can sort the array using a custom comparator:

fun main() {
    println(
        arrayOf<Any>("Apple",46,"287",574,"Peach","3","69",78,"Grape","423")
            .sortedWith(
                compareBy<Any> {
                    it is String && it.toLongOrNull() == null
                } then compareBy {
                    when {
                        it is Number -> it.toLong()
                        it is String && it.toLongOrNull() != null -> it.toLong()
                        else -> it.toString()
                    }
                }
            )
    )
}

Output: [3, 46, 69, 78, 287, 423, 574, Apple, Grape, Peach]

Upvotes: 1

Bob Redity
Bob Redity

Reputation: 621

So just had to make some manipulation

val onlyNumbers = a.filterIsInstance<Int>()
val allStrings = a.filterIsInstance<String>().sorted()
val onlyStrings = mutableListOf<String>()
val listOfEditedNumbers = mutableListOf<Int>()
allStrings.forEach {
    try {
        val toInt = it.toInt()
        listOfEditedNumbers.add(toInt)
    } catch (e: java.lang.Exception){
        onlyStrings.add(it)
    }
}
val allNumbers = onlyNumbers.toList() + listOfEditedNumbers
val finalSortedNumberList = allNumbers.sorted()
val finalNumbersList = mutableListOf<Any>()
finalSortedNumberList.forEach {
    if (it in onlyNumbers) finalNumbersList.add(it)
    if (allStrings.contains(it.toString())) finalNumbersList.add(it.toString())
}
val finalTotalList = finalNumbersList + onlyStrings.sorted()

Upvotes: 0

Related Questions