Kotlin Kandy: Transform Items In A Collection To A Map With associate
Kotlin gives us the associate
method for collection objects, like lists, iterables and arrays. With this method we can convert the items in the collection to a new Map
instance. The associate
method accepts a lambda function as argument and we must return a Pair
from the lambda. The first item of the pair will be the key and the second element is the value of the key/value pair in the resulting map.
If we want to use the elements in our collection as key, but want to transform the value we must use associateWith
. The lambda for this method must return the value part of our key/value pair. Alternatively if we only want to transform the key value we can use associateBy
with one lambda function. The lambda function must return the result for the key in the key/value pair of the map. The method associateBy
is overloaded where we can pass two lambda functions. The first lambda function is for transforming the key and the second lambda function is for transforming the value.
In the following example we use all variants of the associate
methods:
// Sample list of string vlaues.
val languages = listOf("Kotlin", "Groovy", "Java", "Clojure")
// With associate we use a lambda to return a Pair.
// The Pair is the key/value for the resulting map.
// In this example we use the item in lowercase as key and
// the length of the item as value.
assert(languages.associate { s -> s.lowercase() to s.length } ==
mapOf("kotlin" to 6, "groovy" to 6,
"java" to 4, "clojure" to 7))
// associateBy accepts a lambda to return the key value
// of a pair and the value is the element from the collection.
// Here we want to use the lowercase value of the item as key.
assert(languages.associateBy(String::lowercase) ==
mapOf("kotlin" to "Kotlin", "groovy" to "Groovy",
"java" to "Java", "clojure" to "Clojure"))
// associateBy accepts a second lambda to also transform
// the value part of the key/value pair.
// With a second lambda we transform the item to it's length as value.
assert(languages.associateBy(String::lowercase, String::length) ==
mapOf("kotlin" to 6, "groovy" to 6,
"java" to 4, "clojure" to 7))
// associateWith accepts a lambda to transform the value part
// of the key/value pair. The key is then the element
// from the collection.
// We use the item of the collectio as key, but use the
// length of the string value as value for our key.
assert(languages.associateWith(String::length) ==
mapOf("Kotlin" to 6, "Groovy" to 6,
"Java" to 4, "Clojure" to 7))
Each of the assiocate methods return a Map
with key/value pairs. If we want to add the result to an existing, mutable Map
instance we must use the methods associateTo
, associateByTo
and associateWithTo
. The first argument is a mutable Map
and the rest of the arguments is the same as for the associate methods without To
.
In the following example we want to add new key/value pairs to an existing Map
using associate
method variants:
// Example list of some numbers.
val numbers = listOf(2, 3, 4)
// Helper function to return the square value of a given number.
fun square(n: Int) = n * n
// For each of the associate/associateBy/associateWith methods there is
// an equivalent associateTo/associateByTo/associateWithTo to add the result
// to an existing mutable map instance.
assert(numbers.associateTo(mutableMapOf(0 to 0, 1 to 1)) { n -> n to square(n) } ==
mapOf(0 to 0, 1 to 1, 2 to 4, 3 to 9, 4 to 16))
assert(numbers.associateByTo(mutableMapOf(0 to 0, 1 to 1)) { n -> n * 10 } ==
mapOf(0 to 0, 1 to 1, 20 to 2, 30 to 3, 40 to 4))
assert(numbers.associateByTo(mutableMapOf(0 to 0, 1 to 1), { n -> n }, ::square) ==
mapOf(0 to 0, 1 to 1, 2 to 4, 3 to 9, 4 to 16))
assert(numbers.associateWithTo(mutableMapOf(0 to 0, 1 to 1), ::square) ==
mapOf(0 to 0, 1 to 1, 2 to 4, 3 to 9, 4 to 16))
Written with Kotlin 1.7.20.