1. Introduction
In this quick tutorial, we’ll see how we can convert a List to a Map in Kotlin.
2. Implementation
Kotlin offers the convenient toMap method which, given a list of complex objects, will allow us to have elements in our list mapped by any values we provide:
val user1 = User("John", 18, listOf("Hiking"))
val user2 = User("Sara", 25, listOf("Chess"))
val user3 = User("Dave", 34, listOf("Games"))
@Test
fun givenList_whenConvertToMap_thenResult() {
val myList = listOf(user1, user2, user3)
val myMap = myList.map { it.name to it.age }.toMap()
assertTrue(myMap.get("John") == 18)
}
Keep in mind that “to” keyword is being used here to create pairs of names and ages. This method should return a map that preserves the entry order of the elements in the array:
{John=18, Sara=25, Dave=34}
The same would happen when we map a smaller array of String:
@Test
fun givenStringList_whenConvertToMap_thenResult() {
val myList = listOf("a", "b", "c")
val myMap = myList.map { it to it }.toMap()
assertTrue(myMap.get("a") == "a")
}
The only difference is that we don’t specify the value for it since it will only be mapped by that.
Then as a second alternative to convert a List to a Map is by using the associatedBy method:
@Test
fun givenList_whenAssociatedBy_thenResult() {
val myList = listOf(user1, user2, user3)
val myMap = myList.associateBy({ it.name }, { it.hobbies })
assertTrue(myMap.get("John")!!.contains("Hiking"))
}
We modified the test so that it uses an array as the value:
{
John=[Hiking, Swimming],
Sara=[Chess, Board Games],
Dave=[Games, Racing sports]
}
3. Which One to Use?
If both methods essentially achieve the same functionality, which one should we use?
toMap, in terms of implementation, is more intuitive. However using this method requires us to transform our Array into Pairs first, which later have to be translated to our Map, so this operation will be particularly useful if we’re already operating on collections of Pairs.
For collections of other types, the associate API will be the best choice.
4. Mapping Using associate* Methods
In our previous example, we used the associateBy method, however, the Kotlin collections package has different versions for different use cases.
4.1. The associate() Method
We’ll start by using the associate method – which simply returns a Map by using a transform function on the elements of the array:
@Test
fun givenStringList_whenAssociate_thenResult() {
val myList = listOf("a", "b", "c", "d")
val myMap = myList.associate{ it to it }
assertTrue(myMap.get("a") == "a")
}
4.2. The associateTo Method
Using this method, we can collect our elements to an already existing map:
@Test
fun givenStringList_whenAssociateTo_thenResult() {
val myList = listOf("a", "b", "c", "c", "b")
val myMap = mutableMapOf<String, String>()
myList.associateTo(myMap) {it to it}
assertTrue(myMap.get("a") == "a")
}
It’s important to remember to use the mutable Map – this example will not work with an immutable one.
4.3. The associateByTo Method
The associateByTo gives us the most flexibility of the three since we can either pass the map that will be populated, a keySelector function. For each specified key, the associated value will be the object where the key was extracted from:
@Test
fun givenStringList_whenAssociateByToUser_thenResult() {
val myList = listOf(user1, user2, user3, user4)
val myMap = mutableMapOf<String, User>()
myList.associateByTo(myMap) {it.name}
assertTrue(myMap.get("Dave")!!.age == 34)
}
Or we can use a valueTransform function:
@Test
fun givenStringList_whenAssociateByTo_thenResult() {
val myList = listOf(user1, user2, user3, user4)
val myMap = mutableMapOf<String, Int>()
myList.associateByTo(myMap, {it.name}, {it.age})
assertTrue(myMap.get("Dave") == 34)
}
It’s important to remember that if key collisions happen, only the last added value is retained.
5. Conclusion
In this article, we explored different ways of converting a List to a Map in Kotlin.
As always, the implementation of all of these examples and snippets can be found over on GitHub.