what’s the best way to pass a Map<String, Any&g...
# announcements
v
what’s the best way to pass a Map<String, Any> by value?
s
pragmatically speaking, you’ll probably want to pass a copy of it or use an ImmutableMap
v
I see so something like .toMap() or .toMutableMap()
its just getting messy putting that everywhere
s
well, oftentimes
toX()
will basically no-op if you’re trying to convert to a type it already is
you’ll want to look into the definition/read the docs there
but, at least in an ideal scenario
if you write a function that takes
Map
and not
MutableMap
, there’s an implicit understanding that you’re not going to try to mutate the map you’ve been given
there’s nothing at runtime to prevent someone attempting to downcast a given
Map
to its underlying (probably mutable) type, but that’s an anti-pattern and you can’t really sanely guard against every instance where someone might do something like that
but like, what’s the scenario here? If you don’t control this function that’s ostensibly mutating your input, then an ImmutableMap will just cause a runtime exception to be thrown when the mutation is attempted. Passing a copy is probably the best option there, unfortunately
v
so essentially, I have a map of key value pairs. that map eventually gets logged out. Each function receiving the map can add values to it and log it out. Its just when I was calling a function in a loop and passing the map in, it would also send the values of the previous run
n
Copy code
val m = mapOf("a" to "A", "b" to 2)

fun f1(m: Map<String, Any>) {
    val m2 = m.toMutableMap()
    m2["c"] = "C"
    println(m2)
}

fun f2(m: Map<String, Any>) {
    val m2 = m.toMutableMap()
    m2["d"] = 4L
    println(m2)
}

println(m)
f1(m)
f2(m)
Something like this?
v
that’s exactly what I ended up doing. you think there is cleaner way?
n
Copy code
fun f2(m: Map<String, Any>) {
    val m2 = mapOf("d" to 4L)
    println(m + m2)
}
is another option
v
probably will stick with option1 for now, since it means I don’t have to worry about concatanation at the end
r
Given your functions are intended to mutate the map, I'd pass in an actual mutable map, plus I'd make it very clear to the caller by appropriate naming i.e.:
Copy code
fun mutateMap1(m: MutableMap<String, Any>) {
    m["c"] = "C"
    println(m)
}
The caller can then wrap all the calls to these functions inside a
buildMap
function (1.3.70+), or similarly encapsulated logic.
v
can you show what the caller would look like? I mean the function which will call mutateMap1, ie. how are we passing it in using buildmap
r
Copy code
val m = buildMap<String, Any> {
  mapFn1(this)
  mapFn2(this)
  ...
}
If your functions are lambdas in a list or part of an interface or something you can of course just invoke them all inside the builder in one line.
v
I see thanks, learned something new, let me see if I can refactor it to do so
r
If you define your functions with a
MutableMap
receiver like this:
Copy code
fun MutableMap<String, Any>.fn1() {
    this["c"] = "C"
    println(this)
}
then you can call them like this:
Copy code
val m = buildMap<String, Any> {
  fn1()
  fn2()
  ...
}
And if you have all of your mutations as a list of lambdas e.g.:
Copy code
val mutations: List<(MutableMap<String, Any>) -> Unit> = ...
then you can build the map like this:
Copy code
val m = buildMap<String, Any> {
  mutations.forEach { apply(it) }
}
I'd also abstract out the
println
from each function and put that in the builder
forEach
instead.