Kotlin .thenBy { it.toString() } not work? - sorting

I try to sort this String: "2000 10003 1234000 44444444 9999 11 11 22 123"
First, I sort by the sum of the numbers. If the sums are equal, I sort them as String. But for some reason sorting like strings(.thenBy { it.toString() }) does not work correctly. Help find out the reason.
fun orderWeight(s: String) = s.split(" ")
.map { it.toInt() }
.sortedWith(compareBy<Int> { it.toString().toCharArray().maxBy { it.toString().toInt() } }.thenBy { it.toString() })
.joinToString(" ")
expected:<11 11 2000 [10003 22] 123 1234000 4444444...> but was:<11 11 2000 [22 10003] 123 1234000 4444444...>
All description: https://www.codewars.com/kata/55c6126177c9441a570000cc/train/kotlin

You'll kick yourself…
The text says:
First, I sort by the sum of the numbers.
But the code says:
it.toString().toCharArray().maxBy { it.toString().toInt() } }
So it's not sorting by the sum of the digits, but by their maximum.  (The second string comparison is fine — but in the case at issue, it's not getting that far, because the max digit differs.)
If you replace maxBy with sumOf, you'll find it works as you require.
Also, as an observation, there's no need for the thenBy; compareBy can accept multiple comparators.  You need to add the parens, but then the compiler can infer the type, so it's marginally simpler overall:
.sortedWith(compareBy(
{ it.toString().toCharArray().sumOf{ it.toString().toInt() } },
{ it.toString() }))

Related

I did not get the Desired Outcome for this Question in Kotlin?

So I was creating an adjacency list from an Undirected Graph
val presentedGraph = listOf(
listOf('i', 'j'),
listOf('k', 'i'),
listOf('m', 'k'),
listOf('k', 'l'),
listOf('o', 'n')
)
The outcome that I was looking for was this
hashMapOf(
'i' to listOf('j', 'k'),
'j' to listOf('i'),
'k' to listOf('i', 'm', 'l'),
'm' to listOf('k'),
'l' to listOf('k'),
'o' to listOf('n'),
'n' to listOf('o')
)
But got this instead
{i=[i], j=[j], k=[k], l=[l], m=[m], n=[n], o=[o]}
Here's the code for it
fun undirectedPath (edges: List<List<Char>>, root: Char, destination: Char){
val graph = buildGraph(edges)
println(graph)
}
fun buildGraph(edges: List<List<Char>>): HashMap<Char, List<Char>>{
val graph = hashMapOf<Char, List<Char>>()
for (i in edges.indices){
for (j in edges[i].indices){
val a = edges[i][j]
val b = edges[i][j]
if (!graph.containsKey(a)) { graph[a] = listOf() }
if (!graph.containsKey(b)) { graph[b] = listOf() }
graph[a] = listOf(b)
graph[b] = listOf(a)
}
}
return graph
}
Any help will be appreciated, Thank You.
Several things wrong here:
The fact that you're setting both a and b to the same expression ought to be a clue that one of them is wrong! In fact a should be set to edges[i][0].
Because j runs from 0, it effectively assumes an extra edge from each node to itself. To avoid that, j should skip the first item and start from 1.
Each time you assign graph[a] and graph[b], you discard any previous items. That's why the result has only one target for each edge. To fix that, you need to add() the target to the existing list…
…which means that each target list must be a MutableList.
Those changes should be enough to get the result you want.
However, there are still several code smells present. For one thing, the input is a list of lists — but each of the inner lists has exactly two items. It would be neater to use a more precise structure, such as a Pair.
And it's always worth being aware of the standard library, which includes a wide range of manipulations and algorithms. In this case, you could replace the whole function with a one-liner:
fun buildGraph(edges: List<Pair<Char, Char>>)
= (edges + edges.map{ it.second to it.first })
.groupBy({ it.first }, { it.second })
As well as being a good deal shorter, that also makes it a good deal clearer what it's doing: combining the list of edges with the reverse list, and returning a map from each node to the list of nodes it connects to/from.
You can try this.
val hashMap = HashMap<Char, ArrayList<Char>>()
presentedGraph.forEach { list ->
list.forEach { char ->
if (!hashMap.containsKey(char)) {
hashMap[char] = arrayListOf()
}
hashMap[char]?.addAll(list.filter { char != it }.toList().distinct())
}
}
println(hashMap)
Output:
{i=[j, k], j=[i], k=[i, m, l], l=[k], m=[k], n=[o], o=[n]}

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?
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!
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]
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]
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()

Sort a Map<Int, Int> by value then by key

I've got a map of values where the key is a digit and the value is the number of the occurrences of the digit in a string
example :
6644241333431
{6:2,4:4,2:1,3:4,1:2}
I would like to get the digit with the most occurrences, if two numbers are equals then get the smaller one.
I feel like it could be done with ".sortedBy{ (key, value) -> ... }" or even "compareBy{ it.first }.thenBy{it.second}".
You can do this almost the way you described:
map.entries
.sortedWith(compareByDescending<Map.Entry<Int, Int>> { it.value }.thenByDescending { it.key })
It returns a list, not a map. You can convert it back to map if you need.
If you only need a single, maximum value, then sorting is really not necessary and you can find it in a similar way as above:
map.maxOfWith(compareBy<Map.Entry<Int, Int>> { it.value }.thenBy { it.key }) { it }

Swift 3 - distinguishing between "for loops"

I am trying to figure out the difference between the following:
for i in A {
and
for i in 0..<A.count {
Don't they both just iterate through the array?
Swift has only one for loop, unlike some other languages. Swift's for loop iterates over a Sequence and provides (in the loop variable, i in your case) each element of that sequence.
In your first example, the sequence is A, the array itself, which means that i is an element of the array:
var A = [ "Mal", "Wash", "Kaylee" ]
for i in A {
print(i) // prints "Mal" on the first pass, then "Wash", then "Kaylee"
}
In your second example, the sequence is a Range of integers, starting at zero and ending at one less than the size of the array. So i is an integer:
for i in 0..<A.count {
print(i) // prints "0", then "1", then "2"
}
If you're using the second style and you need the array element, you need to explicitly fetch it from the array in the loop body:
for i in 0..<A.count {
print(A[i]) // prints "Mal", then "Wash", then "Kaylee"
}
This can be useful for situations where you care at least as much about indices as content:
for i in 0..<A.count {
if A[i] == "Wash" {
A.insert("Zoe", at: i + 1)
}
}
Although, if you need both the elements and indices in a sequence, the preferred Swift style is to use enumerated() instead — this provides a sequence of tuples, giving you both:
for (index, element) in A.enumerated() {
print(index, element) // prints "0 Mal", "1 Wash", "2 Kaylee"
}

Can I use 'where' inside a for-loop in swift?

Is there also a possibility to use the 'where' keyword in another place then a switch? Can I use it in a for in loop for example?
I have an array with bools, all with a value, can I do something like this:
var boolArray: [Bool] = []
//(...) set values and do stuff
for value where value == true in boolArray {
doSomething()
}
This would be a lot nicer than use an if, so I am wondering if there is a possibility to use where in combination with a for-loop. Ty for your time.
In Swift 2, new where syntax was added:
for value in boolArray where value == true {
...
}
In Pre 2.0 one solution would be to call .filter on the array before you iterate it:
for value in boolArray.filter({ $0 == true }) {
doSomething()
}
A normal for-loop will iterate all elements present in the list. But sometimes we want to iterate only when data satisfy some condition, there we can use where clause with for -loop. It's just a replacement of if condition inside the loop.
For example:
let numbers = [1,2,3,4,5,6,7]
for data in numbers {
if (data % 2 == 0) {
print(data)
}
}
can be rewritten in the simpler way as:
for data in numbers where data % 2 == 0 {
print(data)
}
Yes, you can use "where" clause with for loop.
let arr = [1,2,3,4,5]
for value in arr where value != 0 {
print(value)
}
Considering your example,
var boolArray: [Bool] = []
//(...) set values and do stuff
for value in boolArray where value == true {
doSomething()
}

Resources