Kotlin Map and Compute Functions - kotlin

I am trying to re-compute values for all entries in a mutable map without changing the keys.
Here is the code (with comments showing what I am expecting)
fun main(args: Array<String>) {
var exampleMap: MutableMap<Int, String> = mutableMapOf(1 to "One", 2 to "Two")
// Before it's {1-"One", 2-"Two"}
with(exampleMap) {
compute(k){
_,v -> k.toString + "__" + v
}
}
// Expecting {1->"1__One", 2->"2__Two"}
}
I don't want to use mapValues because it creates a copy of the original map. I think compute is what I need, but I am struggling to write this function. Or, rather I don't know how to write this. I know the equivalent in Java (as I am a Java person) and it would be:
map.entrySet().stream().forEach(e-> map.compute(e.getKey(),(k,v)->(k+"__"+v)));
How can I achieve that in Kotlin with compute ?
Regards,

The best way to do this is by iterating over the map's entries. This ensures you avoid any potential concurrent modification issues that might arise due to modifying the map while iterating over it.
map.entries.forEach { entry ->
entry.setValue("${entry.key}__${entry.value}")
}

Use onEach function:
var exampleMap: MutableMap<Int, String> = mutableMapOf(1 to "One", 2 to "Two")
println(exampleMap)
exampleMap.onEach {
exampleMap[it.key] = "${it.key}__${it.value}"
}
println(exampleMap)
Output:
{1=One, 2=Two}
{1=1__One, 2=2__Two}
onEach performs the given action on each element and returns the list/array/map itself afterwards.

Related

Better way to transform immutable kolin Map<K,V>

I have a nested immutable kotlin Map<K, Map<K,V>> which I would like to loop through each entry and perform some transformation and produce a new Map<K, Map<K,V>> which transformed key, values.
Here is my current solution but I feel like there would be a better way to achieve this.
fun main() {
// I would like to loop through each key,value pair and transform them and produce a new map with transformed values.
val mapToTransform = mapOf(1 to mapOf("one" to "I"), 2 to mapOf("two" to "II"))
// Here is my current solution to achieve it. Is there any better way to do this?
val transformedMap = mapToTransform.map { (outerMapKey, innerMap) ->
outerMapKey+1 to innerMap.map { (innerMapKey, innerMapValue) ->
innerMapKey.uppercase() to "$innerMapValue is Roman letter"
}.toMap()
}.toMap()
println(transformedMap)
}
First, I should say that I think your current code is perfectly fine and readable. I would not change it. However, since you don't seem to like using toMap or creating pairs,
I feel instead of creating pairs and using .toMap() twice there should be a cleaner way.
It is possible to not use toMap or create pairs by repeatedly mapKeys and mapValues:
val transformed = mapToTransform
.mapKeys { (outerMapKey, _) -> outerMapKey + 1}
.mapValues { (_, innerMap) ->
innerMap
.mapKeys { (innerKey, _) -> innerKey.uppercase() }
.mapValues { (_, innerValue) -> "$innerValue is Roman letter" }
}
This might be a little more readable depending on who you ask. On the other hand, it creates a lot of intermediate Maps. I'm not sure about how the performance of this compares to your original code, but it probably depends on the contents of the map itself.

Trying to get a when expression to show the result on the ui in kotlin

I have been stuck making this metric converter app for over a week I keep going back to the previous codelabs that explain how to make a tip calculator but when I try to apply the same method to this app that I'm trying to make it doesn't have any errors but the app crashes whenever I try to test it to see if I can get the functionality to work this is my last resort. I've tried to do when statements but I kept getting the error that certain branches would never be reached. if anyone can point me in the right direction as to what I'm doing wrong or what's missing from my code. I'm all ears.
package com.example.metricconversion
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.example.metricconversion.databinding.ActivityMainBinding
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(R.layout.activity_main)
setContentView(binding.root)
binding.conversionTo.setOnClickListener { convertMetric() }
}
fun convertMetric() {
val stringInTextField = binding.howMuchStuffWeTalking.text.toString()
val amountIQ = stringInTextField.toDouble()
val selectedId = binding.unitThatNeedsToBeConverted.checkedRadioButtonId
val selectedId2 = binding.toBeConverted.checkedRadioButtonId
// val grainToQtr = R.id.Grain and R.id.quarter1
//val grainToSt= R.id.Grain and R.id.quarter1
//val grainToLb = R.id.Grain and R.id. pound1
//val grainToTon= R.id.Grain and R.id.Ton1
val st = R.id.Grain and R.id.stone1
// code to convert grain to the other options
//code to convert grain to the other options
when (selectedId and selectedId2) {
R.id.Grain and R.id.Ounce1 -> {
amountIQ / 437.5
}
}
when (selectedId and selectedId2) {
R.id.Grain and R.id.quarter1 -> amountIQ * 0.000005714
}
when (selectedId and selectedId2) {
R.id.Grain and R.id.pound1 -> amountIQ * 0.0001429
}
when (selectedId and selectedId2) {
R.id.Grain and R.id.Ton1 -> amountIQ / 1.4e+7
}
when (selectedId and selectedId2) {
st -> {
amountIQ * 0.0000102
}
}
binding.metricConverted.text=getString(R.string.end_result,convertMetric())
}
}
Problems with your code:
Using and to check two values for equality at the same time is not viable. The and function does a bitwise operation to merge the two numbers together. There are many possible inputs that could merge to the same solution, so you will get false positives. Instead you could use the to infix function to combined the two numbers into a Pair wrapper object so they are both preserved for the comparison.
You have a series of individual when statements, each with only a single condition. It doesn't make sense to use a when statement with a single condition. Usually a single condition would be represented with an if statement instead of when statement. But I'm guessing you didn't mean for these to be separate when statements, based on what I'm seeing.
Your when statements are not actually doing anything. Their branches resolve to a number, but you're not doing anything with that number (not assigning it to a variable or logging it or showing it in the UI, etc.). So they are useless. Your statement at the bottom isn't getting the result of any of the when statements, but is instead recursively calling the same function again. This function doesn't return anything, so that's useless. Even if it did return a number, the recursive call will create an infinite loop, resulting in a stack overflow.
So first, to just fix your code (we can discuss a better way of doing it later):
We replace and with to.
We merge all the when statements into a single statement.
We store the result of the when statement in a variable and use that variable to set the text in the last line of the function. When you use a when statement with a subject (something in parentheses that is compared for each branch) or to get a result, you have to cover every possible case, so an else branch must also be added.
fun convertMetric() {
val stringInTextField = binding.howMuchStuffWeTalking.text.toString()
val amountIQ = stringInTextField.toDouble()
val selectedId = binding.unitThatNeedsToBeConverted.checkedRadioButtonId
val selectedId2 = binding.toBeConverted.checkedRadioButtonId
// code to convert grain to the other options
val result = when (selectedId to selectedId2) {
R.id.Grain to R.id.Ounce1 -> amountIQ / 437.5
R.id.Grain to R.id.quarter1 -> amountIQ * 0.000005714
R.id.Grain to R.id.pound1 -> amountIQ * 0.0001429
R.id.Grain to R.id.Ton1 -> amountIQ / 1.4e+7
R.id.Grain to R.id.stone1 -> amountIQ * 0.0000102
else -> error("Unsupported selection pair")
}
binding.metricConverted.text = getString(R.string.end_result, result.toString())
}
Above, the error() call will crash your app. You need to make sure you cover every possible combination that could occur. During development, this is suitable, but for production you might want to change the behavior so it shows an error message in the UI of the app and doesn't crash.
Now, regarding the overall design, it is quite fragile because you have UI layout details so tightly coupled to your app's behavior. All these formula calculations should probably be defined in a separate class, possibly an Enum class. You could create a Map in your Activity file that links the UI elements to the behavior class(es), and then in your when statement, you could use the UI elements to pull the associated behavior from the map. This would be more maintainable, and make it easier for you to avoid forgetting something as you add/modify functionality. I say this just to get you thinking about it, but it's probably too much for a beginner project right now. I don't have time to explain in detail how I would do all of that for your case.

How to push results from map to multiple list in Kotlin?

I have the same operation from two list and want to put them in one map function rather than two.
val messages1 = list.map {
spConverter.mapToSppmFromPs(it)
}
val messages2 = list.map {
spConverter.mapToSpFromPs(it)
}
Is there a way to put this two operation into one map?
The map function can only create one list, so you can't have it create messages1 and messages2 which are two lists. Perhaps this code will suit:
val (messages1, messages2) = list
.map { Pair(spConverter.mapToSppmFromPs(it), spConverter.mapToSpFromPs(it)) }
.unzip()
First, the map function creates a list of pairs, and then the unzip function runs over that list and creates the two lists that you want.
Unless your list is actually something that can only be iterated once, I would prefer your original code.
Well, not really.
.map returns ONE result. So yes, you could have:
val messages1 = mutableListOf()
val messages2 = list.map {
messages1.add(spConverter.mapToSppmFromPs(it))
spConverter.mapToSpFromPs(it)
}
But that is just ugly. I think running forEach somewhere in the code will be better.

Async Wait Efficient Execution

I need to iterate 100's of ids in parallel and collect the result in list. I am trying to do it in following way
val context = newFixedThreadPoolContext(5, "custom pool")
val list = mutableListOf<String>()
ids.map {
val result:Deferred<String> = async(context) {
getResult(it)
}
//list.add(result.await()
}.mapNotNull(result -> list.add(result.await())
I am getting error at
mapNotNull(result -> list.add(result.await())
as await method is not available. Why await is not applicable at this place? Instead commented line
//list.add(result.await()
is working fine.
What is the best way to run this block in parallel using coroutine with custom thread pool?
Generally, you go in the right direction: you need to create a list of Deferred and then await() on them.
If this is exactly the code you are using then you did not return anything from your first map { } block, so you don't get a List<Deferred> as you expect, but List<Unit> (list of nothing). Just remove val result:Deferred<String> = - this way you won't assign result to a variable, but return it from the lambda. Also, there are two syntactic errors in the last line: you used () instead of {} and there is a missing closing parenthesis.
After these changes I believe your code will work, but still, it is pretty weird. You seem to mix two distinct approaches to transform a collection into another. One is using higher-order functions like map() and another is using a loop and adding to a list. You use both of them at the same time. I think the following code should do exactly what you need (thanks #Joffrey for improving it):
val list = ids.map {
async(context) {
getResult(it)
}
}.awaitAll().filterNotNull()

How can max() sometimes be a lot faster on a sequence of the same length?

I'm asking this question solely to better understand how kotlin sequences work. I thought I had a solid grasp, but I cannot explain what I observed in a short test by what I know, so obviously I have a misconception somewhere.
My goal was to do a quick benchmark to compare the performance of lists vs. sequences when filtering for a criteria and then taking the maximum value of the result. This is an operation that occurs fairly often in some code I have, and I'm trying to decide whether or not it's worth rewriting it to use sequences instead of lists. It seems it would be, as sequence is consistently faster, but that is not the question here.
Rather, I would ask you to explain to me how the below described "artifact" can come about.
First of all, here's the complete test I ran:
fun `just checking the performance of sequences`() {
val log = logger()
var totaldif = 0L
var seqFasterThanList = 0
(1..1000).forEach {
val testList = (1..6000000).toList().shuffled()
val testSequence = testList.asSequence()
// filter and find max
val listDuration = measureTimeMillis {
testList.filter { it % 2 == 0 }.max()
}
val sequenceDuration = measureTimeMillis {
testSequence.filter { it % 2 == 0 }.max()
}
log.info("List: {} ms; Sequence: {} ms;", listDuration, sequenceDuration)
if (sequenceDuration < listDuration) {
seqFasterThanList++
totaldif += (listDuration - sequenceDuration)
}
}
log.info("sequence was faster {} out of 1000 runs. average difference: {} ms",
seqFasterThanList, totaldif / seqFasterThanList)
}
The results mostly looked like this:
List: 299 ms; Sequence: 217 ms;
List: 289 ms; Sequence: 211 ms;
List: 288 ms; Sequence: 220 ms;
Except, every once in a while, about 1 in 20, the result looked more like this:
List: 380 ms; Sequence: 63 ms
As you can see, in these cases the operation was vastly faster. This is the kind of behaviour I would expect on operations like find or first, which can terminate early once they find a match. But by its very nature, max has to traverse the entire sequence to guarantee the result. So how is it possible that some times it can find a result more than 3 times as fast as it usually requires, with the same number of elements to traverse?
Further down is my original answer which, as #Slaw pointed out, wasn't actually answering what you asked (it was explaining why Sequence.filter is faster than Iterable.filter, not why Sequence.filter seems to be intermittently faster than it normally is). However, I'm leaving it below as it's related to what I think might be the answer to your actual question.
My guess is this might be related to garbage collection. As you can see from my original answer, when you call Iterable.filter you are causing lots of arrays to be copied, i.e. you're putting lots of stuff in memory, which has to be cleaned up at certain points. I wonder if it's this cleanup of stuff in memory created by the List tests, which is actually causing the anomalies. I think what might be happening is that every so often the garbage collector kicks in and does a full collection: this is causing the List test to slow down to slower than normal. And after this runs the memory is all cleaned up, which might be why the Sequence test is faster that time.
And the reason I suspect it's related to garbage collection is because I replicated your anomalies, then made one change: instead of calling testList.filter I call testList.filterTo, passing in an ArrayList of the same size as the list. That means that no array copying has to happen, and also the creation of the ArrayList is now outside of the timing:
val arrayList = ArrayList<Int>(testList.size)
val listDuration = measureTimeMillis {
testList.filterTo(arrayList) { it % 2 == 0 }.max()
}
As soon as I did that, the anomalies disappeared. Maybe you can check on your system and see if this makes the anomalies disappear there too. It's intermittent so a bit difficult to know for sure.
This doesn't prove that it's garbage collection, of course, but I think it makes it a possible culprit. You could turn on GC logging to see if you wanted to know for sure. If you do, let us know what you find: it would be interesting to hear your results.
Original answer below (explaining why Iterable.filter is slower than Sequence.filter)
If you look at the source code for Iterable<T>.filter you'll see it does this:
public inline fun <T> Iterable<T>.filter(predicate: (T) -> Boolean): List<T> {
return filterTo(ArrayList<T>(), predicate)
}
It creates a new ArrayList then loops round the items, checking the predicate against each one, and adding them to that array list if they match the predicate. This means that every X items (whatever the array list's default size is), the array list has to resize itself to allow more items in (i.e. create a new copy of the underlying array in which it's storing all its data).
In a sequence, however, the code is different:
public fun <T> Sequence<T>.filter(predicate: (T) -> Boolean): Sequence<T> {
return FilteringSequence(this, true, predicate)
}
Here there isn't some underlying array storing all the items, so no copying of arrays has to take place. Instead, there's just an Iterator which will return the next item which matches the predicate whenever next is called.
You can see the details of how this is implemented in the FilteringSequence class:
internal class FilteringSequence<T>(
private val sequence: Sequence<T>,
private val sendWhen: Boolean = true,
private val predicate: (T) -> Boolean
) : Sequence<T> {
override fun iterator(): Iterator<T> = object : Iterator<T> {
val iterator = sequence.iterator()
var nextState: Int = -1 // -1 for unknown, 0 for done, 1 for continue
var nextItem: T? = null
private fun calcNext() {
while (iterator.hasNext()) {
val item = iterator.next()
if (predicate(item) == sendWhen) {
nextItem = item
nextState = 1
return
}
}
nextState = 0
}
override fun next(): T {
if (nextState == -1)
calcNext()
if (nextState == 0)
throw NoSuchElementException()
val result = nextItem
nextItem = null
nextState = -1
#Suppress("UNCHECKED_CAST")
return result as T
}