Is there a better way to build a map from 2 lists ...
# getting-started
y
Is there a better way to build a map from 2 lists such that it's based on their order in the list? Currently, I'm doing
list1.zip(list2).toMap()
but it turns them into pairs in the process and I don't quite like that. Is there a neater 1-liner for it? I looked at associateWtih and associateBy but I can't seem to get them to work for this use case. Edit: Neatest one-liner I've found:
(0..list1.lastIndex).associateBy(list1::get, list2::get)
c
A map is a collection of Pairs to hold the key, value mappings - no way around that. Some map types have no defined order (HashMap), others are insertion-order based (LinkedHashMap), others are Comparable/Comparator based (TreeMap).
y
Yes but I mean that the intermediate step where
zip
turns it into a
List<Pair<A, B>>
. I want to avoid the creation of that list. In other words, I just want the map to be directly created out of my initial 2 lists. I can do this imperatively like so:
Copy code
buildMap {
    for(i in 0..list1.lastIndex){
        put(list1[i], list2[i])
    }
}
But I would like something neater
c
perhaps
(list1 + list2).toMap
(or
associateBy
etc)
y
Got it!
(0..list1.lastIndex).associateBy(list1::get, list2::get)
e
Copy code
list1.asSequence().zip(list2.asSequence()).toMap()
y
Wouldn't that still create intermediate `Pair`s though? That's mainly what I'm trying to avoid
e
depends on what you are after. it avoids the
List
but has other costs, including
Pair
.
y
The intermediate list is fine enough for me, the Pairs feel inelegant though. Is the `(0..list1.lastIndex)`clear enough, in your opinion? Or is it hard to understand at first glance?
e
list1.indices
is undoubtedly clearer, but I'd use
0 until minOf(list1.size, list2.size)
for consistency with
zip
I'd be more concerned about the list (which is built with arrays and mutation internally) than the pairs (short-lived immutable objects, which the GC can easily reclaim while in the nursery) but what matters more is somewhat situational
y
Oh yeah right, I completely forgot about
indices
. Thanks for all the suggestions. Tbh currently performance isn't that big of a goal, but obviously I don't want to write inefficient code that could very easily be optimised. I think going with indices will potentially be the most efficient (because an
IntRange
seems quite cheap) and I think I might just define a
fun maxOf(IntRange, IntRange)
To top it all off!
e
for the record, Kotlin will optimize away the
IntRange
in
for (i in list1.indices)
or
for (i in 0 until maxOf(list1.size, list2.size))
, but it won't (ending up going through the
Iteratable
and
Iterator
interfaces) in
list1.indices.associateBy
or a custom
maxOf(IntRange, IntRange)