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

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.

Related

Kotlin Map and Compute Functions

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.

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.

Grouping and ordering the top 3 values in a list

I have a list similar in concept to the following:
val selectedValues = listOf("Apple", "Apple", "Apple", "Grape", "Grape", "Cherry")
I need a way to group and sort it so that I get something like this:
Apple: 3
Grape: 2
Cherry: 1
I got a little bit of headway with this answer but I want it to be ordered by the count (most to least) and I can't seem to figure out how to get there.
I feel like the answer I posted gets me very close to what I want but I just need to figure out how to get it to work the way I need to and I'm just hitting a wall and need a little assistance as I'm still fairly new to Kotlin.
You could try something like this:
fun main(args : Array<String>) {
val selectedValues = listOf("Apple", "Apple", "Apple", "Grape", "Grape", "Cherry")
val solution = selectedValues
.groupBy { it }
.mapValues { it.value.size }
.toList()
.sortedByDescending { (_, value) -> value }
.toMap()
println(solution)
}
This will returns you
{Apple=3, Grape=2, Cherry=1}
which I think is (more or less) what you were after.
If you want to get a bit clever, you can use a Grouping which allows you to do a group-and-fold operation, i.e. group a bunch of things and then turn each group into a result - there's an eachCount() operation which turns each group into a count:
selectedValues.groupingBy { it }.eachCount()
.entries
.sortedByDescending { it.value }
.take(3) // they're still Entry pairs here, so you can toMap() if you need one
.run(::println)
Stefano's approach is the most general (and definitely what I'd go for if you're still starting out), but there are some specific tools for some scenarios too (possibly with some performance optimisation going on under the hood)
There's also the "build your own map of counts" approach:
// this is basically the same as "val newMap, stuff.forEach { update count in newMap }"
selectedValues.fold(mutableMapOf<String, Int>()) { counts, item ->
counts.apply { put(item, getOrDefault(item, 0) + 1) }
}
which I think is how the groupingBy().eachCount() combo above works - instead of creating a map of lists ("Apple"=["Apple", "Apple", "Apple"] and so on) and then generating a map of Ints from that, you just generate the Map<String, Int> from the original source list.
Whatever's easiest honestly, you only need to worry about efficiency when you're dealing with a lot of stuff, and Kotlin lets you write nice readable operations with its basic functions. Just thought I'd give you an idea of some of the options you have since you were struggling a little, in case it helps some things click!

Is it considered bad convention when in iterating through two maps, I don't check if key exists in one of them?

I have two maps, let's call them oneMap and twoMap.
I am iterating through all the keys in oneMap, and if the key exists in twoMap I do something
like
fun exampleFunc(oneMap: Map<String, Any>, twoMap: Map<String, Any>) {
for((oneMapKey, oneMapVal) in oneMap) {
if (twoMap.containsKey(oneMapKey)) {
val twoMapVal = twoMap[oneMapKey]
if (twoMapVal == oneMapVal) {
//do more stuff
}
//do more stuff, I have more if statements
}
}
}
To avoid having more nested if statements, I was wondering if instead I could get rid of the
if (twoMap.containsKey(oneMapKey)) check. if twoMap doesn't contain the oneMapKey, we get a null object, and my code still works fine. I was wondering if this is considered bad convention though
fun exampleFunc(oneMap: Map<String, Any>, twoMap: Map<String, Any>) {
for((oneMapKey, oneMapVal) in oneMap) {
val twoMapVal = twoMap[oneMapKey]
if (twoMapVal == oneMapVal) {
//do more stuff
}
//do more stuff, I have more if statements
}
}
It depends. Do you wanna execute the "more stuff" or not?
If you do not wanna execute it you should keep the if condition. Though, if you are concerned about indentation (and deep if hierarchies) you can consider breaking out of the loop:
for((oneMapKey, oneMapVal) in oneMap) {
if (!twoMap.contains(oneMapKey)) continue // continue with next iteration
// do more stuff
}
If your map does not contain null values you can also get the value and check if the result was null (which means the key was not present in the map):
for((oneMapKey, oneMapVal) in oneMap) {
val twoMapVal: Any = twoMap[oneMapKey] ?: continue // continue with next iteration
// do more stuff
}
So its always good practice the remove useless code and (in my opinion) to have less if-hierarchies, as you can easily loose focus when you have lots of nested conditions.
As Tenfour04 says, omitting the containsKey() check is only an option if the map values aren't nullable; if they are, then []/get() gives no way to distinguish between a missing mapping and a mapping to a null value.
But if not (or if you want to ignore null values anyway), then I'd certainly consider omitting the check; the resulting code would be slightly shorter and slightly more efficient, without losing clarity or maintainability.  It could also avoid a potential race condition.  (Though in a multi-threaded situation, I'd be considering more robust protection!)
One variation is to use let() along with the safe-call ?. operator to restrict it to non-null cases:
for ((oneMapKey, oneMapVal) in oneMap) {
twoMap[oneMapKey]?.let { twoMapVal ->
if (twoMapVal == oneMapVal) {
// Do more stuff
}
// Do more stuff
}
}
Using ?.let() this way seems to be a fairly common idiom in Kotlin, so it should be fairly transparent.

How to avoid nested Single in RxJava2

I am fairly new in RxJava pradigm. I am doing following is leading to nested Single objects.
tickHappened.map{
func(it)
}
//I get Single<Single<ArrayList<String>>>
Here tickHappened:Single<T> and func<T>(T param):Single<ArrayList<String>>
tickHappened.map{
func(it)
}
//I get Single<Single<ArrayList<String>>>
.map { single ->
single.map { list ->
list.size
}
}
I actually need to return Single<Int> which is the size of the Arraylist passed. I need to use map twice in the above chain which leads to Single<Single<Int>>
Is there a way to avoid nesting Singles? If I understand Rxjava, it doesn't make sense to have a Single which enclose another Single? If not, then is there a way to return Single<Int>?
As a beginner, one thing to learn is the flatMap operator that is available all around RxJava and is the most common operator needed for solving problems:
tickHappened
.flatMap { func(it) }
.map { it.size() }