When using maps in Kotlin I often find myself writ...
# announcements
n
When using maps in Kotlin I often find myself writing things out like
myMap[k] = myMap[k]!! + 1
or similar, to update the map. Kotlin doesn't seem to allow things like
myMap[k]!! += 1
either. Wouldn't it be useful to have a
modify
or
update
function? Is there one already that I missed? It clarifies intent and avoids the repetition of the key:
Copy code
fun MutableMap<K, V>.update(k: K, transform: (V) -> V) { this[k] = transform(getValue(k)) }
fun MutableMap<K, V>.update(k: K, default: V, transform: (V) -> V) { this[k] = transform(getOrDefault(k, default)) }
Usage would mean that instead of
myMap[k] = myMap[k]!! + 1
you'd be able to write
myMap.update(k) { it + 1}
m
What would
update
do if the key doesn’t exist? Throwing seems an easy to miss error 🤔
n
well, there's two versions, one uses a default, one does not
getValue
also throws
You could always change the non-defaulted one's name to
updateOrThrows
if you wanted, seems overkill to me but eh
m
I didn’t even remember that it existed 😅 Never using anything but
[ ]
or
getOr…
.
I’d prefer overkill over unexpected runtime errors 🙂
n
Sure, the names aren't that critical to me. Th estatement of intent and reducing repetition are more important.
I mean you inevitably run into cases where you already know for sure that the key is in the map
m
What are the use cases you had? I never had that situation so far.
n
unless there's a major logic error. In those cases it's better to use getValue, than []!!
I literally just wrote advent of code day 1 and had these use cases everywhere 🙂
m
With real-world code or a synthetic example? 😄
Where can I find your article?
n
article?
m
Or what is “advent of code” 😄 Thought it’s an article series
n
no, it's a series of coding challenges, i thought I'd try it in kotlin for fun
m
ah okay
n
Anyway, I've seen plenty of code like that in real life
m
Great, so add real-world use cases to your proposal 🙂
n
lol ok, this is updating a value in a map, this happens all the time. It's like asking for real world use cases for getOrPut
m
Again, I’ve never had that issue in years of coding Kotlin. So you and I must work on different kinds of software and use cases help people understand your issue better.
n
I don't know honestly how to respond, for me it's the same as if you said you don't use Maps to begin with, you never needed them in years of coding; how would you start a response to something like that?
m
Giving real-world examples where Maps were really helpful to me 🤔
n
A quick google search will find multiple people asking on SO how to do this, and if there's a better way
okay, you probably use
map
quite frequently, yeah?
m
sure
n
so, this is the same thing, the only difference is you're updating one entry instead of the whole container
sometimes you're storing some data in memory and you're processing updates one at a time instead of all at once
m
.map()
returns copies and doesn’t mutate anything.
n
yes...
e
yeah I can't say I've run into situations where I need to handle this either
it's not like
Copy code
myMap[k] = myMap.getOrElse(k) { 0 } + 1
is very onerous
n
I mean a lot of other things are also not onerous, but we still have convenience functions for them
e
I suppose you could look to the Java 9+ Map API…
n
it's not very onerous to just use a for loop instead of
map
e
well, there is a obvious (to me) value in breaking down loops into composable parts
for single operations on Map... harder to see
m
it’s not very onerous to just use a for loop instead of map
Actually it is, for example when you use chaining. Function chaining is one of Kotlin’s strong points.
n
it's still not onerous. try it 🙂
q
This is exactly what the
Map#compute
function is for isn't it?
👍 2
You compute a value to put in the map based on the possibly absent current value.
e
or
Map#merge
, depending on your use case
n
ah yes, that seems similar based on a quick google
this is java though right, or is it in kotlin too?
e
myMap.merge(k, 1) { old, new -> old + new }
will put 1 if absent, or increment by 1
n
I guess a better API would actually be:
Copy code
fun MutableMap<K, V>.update(k: K, transform: (V?) -> V) { this[k] = transform(this[k])) }
that way, no exception is thrown unless you specifically ask for it in the transform
e
it's Java, but it's accessible in Kotlin if you're running on the JVM
q
Also, if you want to always have the same default value for a missing key you can use
Map#withDefault
https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/with-default.html
n
It's not the same default. In this case I actually wanted to count occurrences with a mutable map, i had reasons for doing this and not using groupBy or what not
Python, C++ would both allow me to simply += 1, Java has compute as you've shown, so kotlin is the only language here with a pretty awkward solution.
e
Python doesn't, unless you use defaultdict or Counter, or using
.get(k, default)
- which is the same as Kotlin's
.getOrElse(k) { default }
n
we're talking about incrementing a value that already exists...
e
it would be nice if
.withDefault()
returned a subtype of
Map
which guaranteed non-null indexing
n
Copy code
In [1]: x = {"helo": 1}                                                                                                                    

In [2]: x["helo"] += 1                                                                                                                     

In [3]: x                                                                                                                                  
Out[3]: {'helo': 2}
kotlin does not allow this
or at least, I haven't figured out a way to do it
e
well, same in Kotlin with !!. succeeds and fails in the same scenarios
n
it doesn't work for me with !!
e
well sure, it doesn't work with
+=
. I mostly avoid that in Kotlin anyway, due to the confusion between var and mutable.
n
i mean, that's great for you, some of us don't make that choice 🙂
m
It’s probably best to pick up existing conversations in the relevant channel: #C0B8Q383C See https://kotlinlang.slack.com/archives/C0B8Q383C/p1457811911000002 and https://kotlinlang.slack.com/archives/C0B8Q383C/p1504862449000396
n
That thread is 3 years old 🙂 probably should just open a new one
but I can put my suggestion in stdlib instead if it' smore appropriate
m
If you answer to an old thread you can select “Also send to #C0B8Q383C”. Then you have a new message in the channel plus made a connection to the existing discussion as it may contain valuable insight and thoughts.
n
This actually doesn't seem to be a thread though, just a set of messages? Unless I'm following the link wrong
but, I guess, same idea
m
Yeah, Slack didn’t have a thread feature back then. Close enough 😄
n
cool, thanks for the suggestion 👍