kotlin: how to use the Intermediate collection when do stream operations - kotlin

for example
val coordinates: Array<IntArray> = [[1, 2], [1, 3], [1, 4]]
coordinates
.map {it[0]}
.all {it == x[0]}
x means the map result of coordinates. I want use it in the 'all' step, how to do that?

You could just write this
coordinates
.map {it[0]}
.all {it == coordinates[0][0]}
Or if you don't want to directly refer to coordinates again, maybe insert a let like
coordinates
.map { it[0] }
.let { x -> x.all { it == x[0] } }
Although for your specific use case I probably would just do
coordinates
.all { it[0] == coordinates[0][0] }

If I understand correctly, you can just declare a val called x:
val x = coordinates.map { it[0] }
val result = x.all { it == x[0] }
Or if you want to do it in one expression, you can use the scope function run or let:
val result = coordinates.map { it[0] }.run {
all { it == this[0] }
}
or:
val result = coordinates.map { it[0] }.let { x ->
x.all { it == x[0] }
}
Though, if you just want to check if the whole list has exactly one unique value, I think this is more readable:
val result = coordinates.map { it[0] }.distinct().size == 1
The above doesn't short-circuit like all. A shortcircuiting version would need Sequence:
val result = coordinates
.asSequence()
.map { it[0] }
.distinct().take(2).count() == 1

Related

Loop through an array and print how many thimes it gets repeated in Kotlin?

I'm leaning Kotlin, and I'm wondering if there is a better, simpler and not that complicated way to do this in Kotlin? I need to Write a Kotlin program that loops through an array and generates a histogram based on the numbers in it.The results should look like this(vertically):
1: *****
2: **
3: **
4:
5: *
And this is what I did:
fun main() {
val myArray: IntArray = intArrayOf(1,2,1,3,3,1,2,1,5,1)
for(num in 1..5){
println("")
print("$num: ")
for(element in myArray) {
if(element == num){
print("*")
}
}
}
}
You can use the eachCount function to find the count of items in a list.
This is what I came up with:
fun main() {
val map = listOf(1,2,1,3,3,1,2,1,5,1)
.groupingBy{ it }
.eachCount()
(1..5).forEach {
print("\n${it}:")
map[it]?.let { count -> repeat(count){ print("*") } }
}
}
You can see the code here: https://pl.kotl.in/91d7pWDGe
Kotlin Koans is a good place you can learn by writing code. Here is the Koans for Collections: https://play.kotlinlang.org/koans/Collections/Introduction/Task.kt
Not sure if it will be simpler, but you can do something like this:
fun main() {
val myArray: IntArray = intArrayOf(1, 2, 1, 3, 3, 1, 2, 1, 5, 1)
for (num in 1..5) {
println("")
val count = myArray.count { it == num }
val entries = if (count > 0) "*".repeat(count) else ""
print("$num: $entries")
}
}

Kotlin: mutable map of mutable list won't update the list

(Kotlin newbie here) I have a text file with rows that look like these:
1-1-1
1-1-2
1-1-3
2-1-1
2-1-2
etc.
I have to transform these data to a map where the key is the first 2 elements and the value is a list of the third elements that that match the key. For example, the above records will transform into this JSON:
1-1: [1, 2, 3]
2-1: [1, 2]
etc.
I'm unable to increment the list. Here's a simplified version, I get stuck on the "else":
fun main () {
val l1 = mutableListOf("1-1-1", "1-1-2", "1-1-3", "2-1-1", "2-1-2")
val m = mutableMapOf<String, List<Int>>()
for (e in l1) {
val c = e.split("-")
val key = "${c[0]}-${c[1]}"
if (m[key] == null) m[key] = listOf(c[2].toInt())
else println("How do I append to the list?")
}
println(m)
}
Output:
{1-1=[1], 2-1=[1]}
But I want:
{1-1=[1, 2, 3], 2-1=[1, 2]}
Thank you (comments about idiomatic form are welcome!)
If we continue to follow your strategy, what you need is for the value type to be a MutableList. Then you can add to the existing MutableList when there's already an existing list for that key:
fun main() {
val l1 = mutableListOf("1-1-1", "1-1-2", "1-1-3", "2-1-1", "2-1-2")
val m = mutableMapOf<String, MutableList<Int>>()
for (e in l1) {
val c = e.split("-")
val key = "${c[0]}-${c[1]}"
if (m[key] == null) m[key] = mutableListOf(c[2].toInt())
else m[key]!!.add(c[2].toInt())
}
println(m)
}
This can be more natural using getOrPut(). It returns the existing MutableList or creates one and puts it in the map if it's missing. Then we don't have to deal with if/else, and can simply add the new item to the list.
fun main() {
val l1 = mutableListOf("1-1-1", "1-1-2", "1-1-3", "2-1-1", "2-1-2")
val m = mutableMapOf<String, MutableList<Int>>()
for (e in l1) {
val c = e.split("-")
val key = "${c[0]}-${c[1]}"
m.getOrPut(key, ::mutableListOf).add(c[2].toInt())
}
println(m)
}
But we can use the map and groupBy functions to create it more simply:
val m = l1.map { it.split("-") }
.groupBy(
{ "${it[0]}-${it[1]}" }, // keys
{ it[2].toInt() } // values
)
You can achieve your desired output with a single call to groupBy of the Kotlin standard library.
val input = listOf("1-1-1", "1-1-2", "1-1-3", "2-1-1", "2-1-2")
val result = input.groupBy(
{ it.substringBeforeLast("-") }, // extract key from item
{ it.substringAfterLast("-").toInt() } // extract value from item
)
The first lambda function extracts the key to group by of every list item. The second lambda function provides the value to use for each list item.
You can also do it by first mapping your values to Pairs and then group them as follows:
fun main(args: Array<String>) {
val input = listOf("1-1-1", "1-1-2", "1-1-3", "2-1-1", "2-1-2")
val result = input.map {
val values = it.split("-")
"${values[0]}-${values[1]}" to values[2]
}.groupBy ({ it.first }) { it.second }
println(result)
}

Concise conversion of List<List<Double>> to Array<DoubleArray>?

I wrote this in Kotlin:
fun fromLists(cells: List<List<Double>>): Matrix {
return Matrix(cells.stream()
.map { x -> x.toDoubleArray() }
.toArray { i: Int -> Array(i, { k: Int -> DoubleArray(k) }) } )
}
Is there any way to reduce repetition in this code?
(Matrix itself is uninteresting, it just wraps an Array<DoubleArray>)
val ex1: Array<DoubleArray> = cells.map { it.toDoubleArray() }.toTypedArray()
// this should be faster, it doesn't create extra List like the previous example
val ex2: Array<DoubleArray> = Array(cells.size) { i -> cells[i].toDoubleArray() }

Unzip for Triple in Kotlin

In kotlin, if I have a pair I can get a list of first values and second values like this:
val(firstList, secondList) = listOfPairs.unzip()
// listOfPairs: List<Pair<String, Long>>
How can I do the same for a Triple?
How bout something like this:
fun <X>List<Triple<X,X,X>>.unzip(): List<List<X>> {
return fold(listOf(ArrayList<X>(), ArrayList<X>(), ArrayList<X>())){ r, i ->
r[0].add(i.first)
r[1].add(i.second)
r[2].add(i.third)
r
}
}
Usage:
val triplesList = listOf(Triple(1,"s",3), Triple(1,"u",3), Triple(1,"p",3))
val (listOne, listTwo, listThree) = triplesList.unzip()
println("ListOne: $listOne")
println("ListTwo: $listTwo")
println("ListThree: $listThree")
/* Output:
ListOne: [1, 1, 1]
ListTwo: [s, u, p]
ListThree: [3, 3, 3]
*/
Try it online!
How about this?
val firstList = listOf(triple1, triple2).map {it.first }
val secondList = listOf(triple1, triple2).map { it.second }
val thirdList = listOf(triple1, triple2).map { it.third }
or
val result = listOf(triple1, triple2).map {
Triple(it.first, it.second, it.third)
}

fold pairs into map of set; create map entries if not existing already

using a list of pairs, want to transform them to a map of sets.
input: list of pairs is like this
listOf(Pair('bob', UGLY), Pair('sue', PETTY), Pair('bob', FAT))
desired output is a map of set where the key is first of pair, and the set is the second
mapOf('bob' to setOf(UGLY, FAT), 'sue' to setOf(PETTY))
I have tried this, but wow this is incredibly verbose. can this be reduced?
fun main(args: Array<String>) {
var m = HashMap<Int, MutableSet<Int>>()
listOf(1 to 1, 2 to 2, 1 to 3).map {
val set = m.getOrPut(it.first, { listOf<Int>().toMutableSet() })
set.add(it.second)
set
}
println (m)
}
-> {1=[1, 3], 2=[2]}
// yet another version, yields the correct result, but I feel a lack of clarity
// that maybe I'm missing a library function that would suit the purpose.
listOf(1 to 1, 2 to 2, 1 to 3).fold(m, {
mapSet, pair ->
val set = mapSet.getOrPut(pair.first, { listOf<Int>().toMutableSet() })
set.add(pair.second)
mapSet
})
-> {1=[1, 3], 2=[2]}
You can use groupBy and then a mapValues like this:
fun main(args: Array<String>) {
val pairs = listOf(Pair("bob", "UGLY"), Pair("sue", "PETTY"), Pair("bob", "FAT"))
val result = pairs
.groupBy { it.first }
.mapValues { it.value.map { p -> p.second }.toSet() }
println(result)
}