I'd like to get a new instance of some Map with the same content but Map doesn't have a built-in copy method. I can do something like this:
val newInst = someMap.map { it.toPair() }.toMap()
But it looks rather ugly. Is there any more smarter way to do this?
Just use the HashMap constructor:
val original = hashMapOf(1 to "x")
val copy = HashMap(original)
Update for Kotlin 1.1:
Since Kotlin 1.1, the extension functions Map.toMap and Map.toMutableMap create copies.
Use putAll method:
val map = mapOf("1" to 1, "2" to 2)
val copy = hashMapOf<String, Int>()
copy.putAll(map)
Or:
val map = mapOf("1" to 1, "2" to 2)
val copy = map + mapOf<String, Int>() // preset
Your way also looks idiomatic to me.
The proposed way of doing this is:
map.toList().toMap()
However, the java's method is 2 to 3 times faster:
(map as LinkedHashMap).clone()
Anyway, if it bothers you that there is no unified way of cloning Kotlin's collections (and there is in Java!), vote here: https://youtrack.jetbrains.com/issue/KT-11221
Add this extension (to convert entries to pairs)
val <K, V> Map<K, V>.pairs: Array<Pair<K, V>>
get() = entries.map { it.toPair() }.toTypedArray()
And then you can easy combine immutable maps using default Kotlin syntax.
val map1 = mapOf("first" to 1)
val map2 = mapOf("second" to 2)
val map3 = mapOf(
*map1.pairs,
"third" to 3,
*map2.pairs,
)
Related
How do I create a list of lists?
I have a mutable list of ints:
val rsrpList = mutableListOf<Int>()
Now I am trying to create a list of lists, like this:
val rsrpList = mutableListOf<Int>()
val rsrqList = mutableListOf<Int>()
val levelList = mutableListOf<Int>()
val listoflists = List<List<Int>>
listoflists.add(rsrpList)
listoflists.add(rsrqList)
listoflists.add(levelList)
but I know this is wrong, because I'm adding a list one at a time, instead of a list of lists. How would I do this?
You can do this with the Kotlin Standard Library. Both List and MutableList can be created to a specific size (3 in this case) and specify a lambda that will initialize each value.
val listOfList = MutableList(3) { mutableListOf<Int>() }
Or:
val listOfList = List(3) { mutableListOf<Int>() }
Update: To initialize a List with precreated lists:
val listOfList = listOf(list1, list2, list3)
Or in your specific case:
val listOfList = listOf(rsrpList, rsrqList, levelList)
And in both cases you can replace listOf with mutableListOf if you want a mutable list as the main type.
Your example is fine (you have an empty list, you're adding lists to that list, you end up with a list of lists!) but if you're trying to avoid mutability by declaring everything at once:
val listOfLists = listOf(rsrpList, rsrqList, levelList)
and you can declare those lists at the same time if you want
val listOfLists = listOf(
listOf(1, 2, 3),
listOf(9, 8, 7),
listOf(7, 7, 7)
)
or you can use mutableListOf if you need any of them to be mutable! The formatting there isn't necessary, I just think it looks clearer how they're nested
In a Kotlin app, I have this method:
fun <VALUE> metadataOf(vararg pairs: Pair<String, VALUE>) =
MetaData.from(pairs.toMap())!!
Which I'm then using like:
metadataOf(
"sId" to message.sId,
"userId" to message.userId
)
I'm trying to write a method which can create the above for me from message - however I'm not sure how to return a list of pairs - this is what I put together based on the input parameter from metadataOf(vararg pairs: Pair<String, VALUE>)
fun metadataFrom( message: CommandMessage<Any> ): Pair<String, Any> {
return (
"sId" to message.sId,
"userId" to message.userId
)
}
You have two choices here.
Using List:
fun metadataFrom(message: CommandMessage<Any>): List<Pair<String, Any>> = listOf(
"sId" to message.sId,
"userId" to message.userId
)
You can use it in this way:
val result = metadataFrom(message)
metadataOf(*result.toTypedArray())
Using Array:
fun metadataFrom(message: CommandMessage<Any>): Array<Pair<String, Any>> = arrayOf(
"sId" to message.sId,
"userId" to message.userId
)
You can use it in this way:
val result = metadataFrom(message)
metadataOf(*result)
The second is more performant since you are directly creating the array used as input for metadataOf but nothing noticeable if you haven't a huge amount of data. So choose your favorite one.
How to collect a stream of pairs in kotlin?
So in Java, I usually do:
Stream.of("1", "2", "3").map(x -> new AbstractMap.SimpleEntry<>(x, x)).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue))
However in Kotlin,
Stream.of("1", "2", "3").map{ x -> x to x }
returns a Stream of Pairs and i can't find a way to collect this.
In Kotlin, it's more natural to use listOf() rather than Java's Stream.of(). Once you have a List<Pair>, you can use the .toMap() extension to turn them into a Map!
val myMap: Map<String, String> = listOf("1", "2", "3").map{ it to it }.toMap()
There is also the .associate() function on List that will just create a map for you, given a lambda:
val myMap2: Map<String, String> = listOf("1", "2", "3").associate { it to it }
That one seems cleaner, IMHO.
Since you use the list element itself as key, using associateWith would make the code even more concise:
val myMap = listOf("1", "2", "3").associateWith{ it }
Result:
{1=1, 2=2, 3=3}
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)
In Kotlin I can say
//sweet
for ((key,value) in System.getProperties())
println("$key = $value")
but I cannot say
//sour
val properties = System.getProperties()
val list = properties.map((key,value) -> "$key = $value")
What is the Kotlin equivalent to properties.map{case (key, value) => s"$key = $value"} in Scala?
In Kotlin 1.0 you can say:
val properties = System.getProperties()
val list = properties.map { "${it.key} = ${it.value}" }
And if you prefer to unpack the map entries to separate values you can say:
val properties = System.getProperties()
val list = properties.map { val (key, value) = it; "$key = $value" }
In Kotlin 1.1 "you can now use the destructuring declaration syntax to unpack the arguments passed to a lambda" (What's New in Kotlin 1.1 - Kotlin Programming Language):
val properties = System.getProperties()
val list = properties.map { (key,value) -> "$key = $value" }
Your question has absolutely nothing with inference.
In scala you doc:
import collection.JavaConversions._
val properties = System.getProperties()
val list = properties.map{case (key,value) => s"$key = $value"}
As for your comments.
Having gone from years of Scala, to now exploring Kotlin, I tend to like Kotlin better in its power and simplicity, however, it would be nice to be able to infer things better, given that I am still in learning mode.
The issues in your code have nothing to do with type inference. Not even the syntax was right.
Please correct your syntax for lambda expressions and unpacking:
val properties = mapOf(1 to "First", 2 to "Second")
val list = properties.map {val (value, key) = it; "$key = $value"}
There is a Kotlin feature that is planned but will not be available in 1.0 which will allow writing {(key,value) -> "$key = $value"} to unpack a value type like a Pair – cypressious
Thanks #cypressious that was exactly what I was hoping for. Basically the answer is that Kotlin is still evolving and people are still getting around to polishing off cool stuff.
For now I have found that
//savory
val properties = System.getProperties()
val list = properties.map {property -> "${property.key} = ${property.value}"}
comes pretty close.