'Order ArrayList with Int and String in Kotlin
I have the given array
val things = arrayOf<Any>("Jack", 8, 2, 6, "King", 5, 3, "Queen", "Jack");
Below is the code I have used
things.set(things.indexOf("Jack"), 11);
things.set(things.indexOf("Queen"), 12);
things.set(things.indexOf("King"), 13);
things.sort();
things.set(things.indexOf(11), "Jack"));
things.set(things.indexOf(12), "Queen");
things.set(things.indexOf(13), "King");
things.joinToString(":");
things.forEach { System.out.print(it) }
Here is the error I keep getting
Unexpected tokens (use ';' to separate expressions on the same line)
Expecting an element
After removing the trailing bracket, I now get the following error
Exception in thread "main" java.lang.ClassCastException: class java.lang.Integer cannot be cast to class java.lang.String (java.lang.Integer and java.lang.String are in module java.base of loader 'bootstrap')
at java.lang.String.compareTo (:-1)
at java.util.ComparableTimSort.binarySort (:-1)
at java.util.ComparableTimSort.sort (:-1)
Solution 1:[1]
You've 2 Jack in your list, so when you use indexOf(), it returns the index of the first occurrence of that item.
things.set(things.indexOf("Jack"), 11);
things.set(things.indexOf("Queen"), 12);
things.set(things.indexOf("King"), 13);
This changes your list to
[11, 8, 2, 6, 13, 5, 3, 12, Jack]
Now, when you apply the sort() operation on things. It throws ClassCastException on Jack as it's a string, not an int.
To find all the indexes of a value, so you can replace them, you can create an extension function that returns a list of all the indices for a value.
fun <T> Array<T>.indexesOfAll(predicate: (T) -> Boolean): List<Int> {
return mapIndexedNotNull { index, any ->
index.takeIf {
predicate(any)
}
}
}
Now, you can use this indexesOfAll function to get the indexes of all the items for a particular condition.
things.indexesOfAll { it == "Jack" }.forEach {
things.set(it, 11)
}
You can do the same for all other values too.
Solution 2:[2]
the problem is that you have "Jack" two times in it. things.set(things.indexOf("Jack"), 11); only changes the first one to 11, not the second one.
val things = arrayOf<Any>("Jack", 8, 2, 6, "King", 5, 3, "Queen", "Jack");
things.set(things.indexOf("Jack"), 11);
things.set(things.indexOf("Queen"), 12);
things.set(things.indexOf("King"), 13);
println(things.contentToString()) //prints [11, 8, 2, 6, 13, 5, 3, 12, Jack]
Now, how I maybe would do it, is like this:
fun main() {
val things = arrayOf("Jack", 8, 2, 6, "King", 5, 3, "Queen", "Jack")
things.sortWith(compareBy { it.toInt() })
println(things.contentToString()) //prints [2, 3, 5, 6, 8, Jack, Jack, Queen, King]
}
val mapping = mapOf("Jack" to 11, "Queen" to 12, "King" to 13)
fun Any.toInt() : Int {
if (this is Int) return this
return mapping[this] ?: 0
}
Solution 3:[3]
Even though the other answers solve the problem at hand, there is a base issue at play. Normally you would want to avoid having multi-typed arrays (or any type of collection).
A better solution would be to introduce and abstraction to represent both types of data as the same class and make that class implement the Comparable interface.
An implementation example of this:
sealed class Card(val name: String) : Comparable<Card> {
abstract val order: Int
override fun compareTo(other: Card): Int =
order - other.order
override fun toString(): String = name
class Number(value: Int) : Card(value.toString()) {
override val order: Int = value
}
object Ace : Card("Ace") {
override val order: Int = 1
}
object Jack : Card("Jack") {
override val order: Int = 11
}
object Queen : Card("Queen") {
override val order: Int = 12
}
object King : Card("King") {
override val order: Int = 13
}
}
then you could use it like this:
println(listOf(Card.Number(2), Card.Ace, Card.Queen, Card.Number(3)).sorted())
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Praveen |
| Solution 2 | |
| Solution 3 | Eric Martori |
