Kotlin, is it possible to access the pair of a map? - kotlin

Is it possible to access the whole Pair of a map, not only the key or a value?
Let's say we have a map
map = mapOf(Pair("Example1", 1), Pair("Example2", 2), Pair("Example3",
3))
I would like to access the second pair and put it into a variable, something like I would do with a list:
val ex2 = map[1] #this would result with {"Example2", 2}
And then i would be able to access the pair's key/value like:
ex2.key / ex2.value
More specifically, I would like to use this in my function to return a specific pair of the map.

Not sure if this would help
val mapString = mutableMapOf(1 to "Person", 2 to "Animal")
val (id, creature) = 1 to mapString.getValue(1)
Log.e("MapPair", "$id, $creature")
prints
1, Person
or if you're iterating through the entire map
mapString.forEach {
val (id, creature) = it.key to it.value
Log.e("MapPair", "$id : $creature")
}
prints
1 : Person
2 : Animal
or using Pair
val key = 1
val pair = Pair(key, mapString.getValue(key))
Log.e("MapPair", "$pair")
prints
(1, Person)
or if you're iterating through the entire map using Pair
mapString.forEach {
val pair = Pair(it.key, it.value)
Log.e("MapPair", "$pair")
}
prints
(1, Person)
(2, Animal)
Update: For iterating through the map you can also go with Destructuring Declarations
val mapString = mutableMapOf(1 to "Person", 2 to "Animal")
for ((key, value) in mapString) {
Log.e("MapComponents", "$key, $value")
}

From your comment, it seems like you want to fetch the key corresponding to a given value.
val map = mapOf("Chicken" to 20, "Egg" to 10, "Bread" to 5)
val valueToFind = 20
val key = map.toList().find { it.second == valueToFind }?.first
println(key)
Output:
Chicken
If the value doesn't exist, it will give null.

Related

How to use an item in a Map<Map<x, y> z>?

I need to make a built-in counter of the number of things through the Map in the Map.
initialization:
var Items = mapOf<Map<String, Item>, Int>()
Example of interaction
for(i in item)
if (this.UnitInventory.Items.containsKey(Pair(i.Name, i) as Map<String, Item>))
this.UnitInventory.Items[Pair(i.Name, i)]++
else
this.UnitInventory.Items += mapOf(Pair(Pair(i.Name, i) as Map<String, Item>, 1))
How to properly write part this.UnitInventory.Items[Pair(i.Name, i)]++?
Like Tenfour04 says in the comments, this is kinda brittle - but so long as you're using immutable Maps as keys it's probably ok? Anyway, if that's what you want, you can do this:
// this -outer- map needs to be mutable (since you'll be adding new counts)
val items = mutableMapOf<Map<String, Int>, Int>()
// make some map keys to count
val someMap = mapOf("One" to 1)
val someOtherMap = mapOf("Two" to 2, "Three" to 3)
val stuff = listOf(someMap, someOtherMap, someMap)
// add them to the counts
stuff.forEach { item ->
// update the current count, defaulting to zero if it doesn't exist yet
items[item] = items.getOrDefault(item, 0) + 1
}
println(items)
>> {{One=1}=2, {Two=2, Three=3}=1}

Having an issue with mutablemap in Kotlin

I'm working on an algorithm type challenge, and i am debugging via print statements and i can't seem to figure out why the the values for keys are not what i am expecting
var mapNums = mutableMapOf<Int, Int>()
//imaginary array
//var nums = [34,28,11,21,3,34,8,7,34,7,31,7,3,28,18]
var count = 0
for (n in nums) {
if (mapNums.containsKey(n)) {
count ++
mapNums[n] = count
} else if (!mapNums.containsKey(n)) {
count = 1
mapNums[n] = count
}
}
println(mapNums)
//prints {34=2, 28=4, 11=1, 21=1, 3=3, 8=1, 7=2, 31=1, 18=1}
as you can see the key and values aren't what theyre supposed to be and i am not sure why.
You can use the following code to generate the desired map:
val nums = intArrayOf(34, 28, 11, 21, 3, 34, 8, 7, 34, 7, 31, 7, 3, 28, 18).toList()
println(nums.groupingBy { it }.eachCount())
try it yourself
Here groupingBy creates a Grouping source using the same element as the key selector. Then eachCount groups elements from the Grouping source by key and counts elements in each group.
You can also refer the documentation for more info about groupingBy and eachCount.
It's because you reuse the same count variable outside of the loop so it keeps incrementing from different keys.
Instead you should get the current count from the map, then put it back one higher:
val nums = intArrayOf(34,28,11,21,3,34,8,7,34,7,31,7,3,28,18)
val mapNums = mutableMapOf<Int, Int>()
for (n in nums) {
val count = mapNums[n] ?: 0
mapNums[n] = count + 1
}
println(mapNums) // {34=3, 28=2, 11=1, 21=1, 3=2, 8=1, 7=3, 31=1, 18=1}
Firstly check n number is contain this map as key, if found then increment 1 its value using plus method. If not found any value from the map, it will null and check if null and set 1.
var mapNums = mutableMapOf<Int, Int>()
//imaginary array
var nums = arrayOf(34,28,11,21,3,34,8,7,34,7,31,7,3,28,18)
for (n in nums) {
mapNums[n] = mapNums[n]?.plus(1) ?: 1
}
println(mapNums)

Sum Mutable Map values inside a list of objects kotlin

I have a data class in Kotlin like this:
data class Activity(
var id: String? = "",
var prize: MutableMap<String?, Int?>? = null
)
And a list of this object:
var myList = listOf(Activity("A", prize={day_5=70, day_4=70}),
Activity("B", prize={day_5=40, day_4=80}))
The desired result is:
Activity("A", prize={total=140}),
Activity("B", prize={total=120})
So basically I want to sum the values of the prize map inside of each object.
I think that has something to do with transformation but I'm new to Kotlin and I couldn't find any resources over the internet, or maybe they were to complicated.
This algorithm is easy, just iterating through all the maps and replace elements with the sum:
val myList = listOf(
Activity("A", hashMapOf("day_5" to 70, "day_4" to 70)),
Activity("B", hashMapOf("day_5" to 40, "day_4" to 80)),
)
for (i in myList) {
val total = i.prize.values.sum()
i.prize.clear()
i.prize["total"] = total
}

Kotlin iterate a collection and map a certain value to new map

I have a map of collections . I need to get a list of ids from that..
val m1 = mapOf("id" to 1, "name" to "Alice")
val m2 = mapOf("id" to 2, "name" to "Bob")
val m3 = mapOf("id" to 3, "name" to "Tom")
val nameList = listOf(m1, m2, m3)
The result shall be [1, 2, 3]
Assuming you want a list as per the example, not a map as per the title, I would do it like this:
val result = nameList.map {
it.getValue("id").also { id ->
require(id is Int) { "id must be an Int" }
} as Int
}
This has the advantage of handling the following errors cleanly:
The id key is missing: NoSuchElementException: Key id is missing in the map
The id value is not an Int: IllegalArgumentException: id must be an Int
First things first, I believe that if you can, you should use classes instead of maps for storing heterogeneous data like this. So instead of your maps, you can use:
data class Person(val id: Int, val name: String)
val m1 = Person(id = 1, name = "Alice")
val m2 = Person(id = 2, name = "Bob")
val m3 = Person(id = 3, name = "Tom")
val list = listOf(m1, m2, m3)
val idsList = list.map { it.id } // no error handling required, rely on the type system
Now, if you really want to use maps like that, you have several options.
If you're certain the id key will be present and its value will be an Int, you can use the following:
nameList.map { it["id"] as Int }
This will fail with NullPointerException if id is not present in one of the maps or with ClassCastException if it's not an Int.
Normally you should make sure your map matches your contract at creation time, and not when accessing this kind of information.
But if you need to handle errors here for some reason, you can use the following instead:
nameList.map {
(it.getValue("id") as? Int) ?: error("'id' is not an Int")
}
getValue fails on absent keys with NoSuchElementException, and the error() call fails with IllegalStateException. You can also use other kinds of exceptions using throw or require().
If you want to just ignore the entries that don't have a valid integer id, you can use the following:
nameList.mapNotNull { it["id"] as? Int }
If you want to ignore the entries that don't have an id, but fail on those who have a non-integer id, you can use this:
nameList.mapNotNull { map ->
map["id"]?.let { id ->
(id as? Int) ?: error("'id' is not an Int")
}
}
These 2 last examples rely on mapNotNull, which filters the elements out if their mapped value is null.

How do I create a map from 2 arrays?

I have a string array and an integer array. How do I create a map using the first as keys and the second as values?
val keys = arrayOf("butter", "milk", "apples")
val values = arrayOf(5, 10, 42)
val map: Map<String, Int> = ???
How to convert List to Map in Kotlin? doesn't solve this problem; I have 2 arrays and want a single map.
You can zip together the arrays to get a list of pairs (List<Pair<String, Int>>), and then use toMap to get your map.
Like this:
val keys = arrayOf("butter", "milk", "apples")
val values = arrayOf(5, 10, 42)
val map: Map<String, Int> =
keys.zip(values) // Gives you [("butter", 5), ("milk", 10), ("apples", 42)]
.toMap() // This is an extension function on Iterable<Pair<K, V>>
According to kotlin
Constructing Collections
-> creating a short-living Pair object, is not recommended only if performance isn't critical and to quote: "To avoid excessive memory usage, use alternative ways. For example, you can create a mutable map and populate it using the write operations. The apply() function can help to keep the initialization fluent here."
since I'm not much of an expert I run on to these code and maybe this should work better?:
val numbersMap = mutableMapOf<String,Int>()
.apply{ for (i in 1.. 5) this["key$i"] = i }
println(numbersMap)
//result = {key1=1, key2=2, key3=3, key4=4}
or to adjust it to question above - something like this:
val keys = arrayOf("butter", "milk", "apples")
val values = arrayOf(5, 10, 42)
val mapNumber = mutableMapOf<String, Int>()
.apply { for (i in keys.indices) this[keys[i]] = values[i] }
println(mapNumber)