Hi guys, I'm still trying to get my head around th...
# arrow
g
Hi guys, I'm still trying to get my head around thinking functionally and I'm stuck with this scenario val toBeRemoved = mapOf("id" to 2) val testList = listOf(Some("id" to 1), None, Some("id" to 3), Some("id" to 1)) //TODO val expectedOutcome = listOf(None, Some("id" to 2), Some("id" to 1)) Can be done with mutability like so val toBeRemovedMutable = toBeRemoved.toMutableMap() val actual = testList.map { item -> val id = item.first var newAmount = item.second for ((key, value) in toBeRemovedMutable) { if (id == key && value > 0) { val remove = if (value > newAmount) newAmount else value toBeRemovedMutable[key] = value - remove newAmount -= remove if (newAmount == 0) { return@map None } } } Some(item.copy(second = newAmount)) } but I can't think of a way to do it more functionally. Any suggestions?
j
A few things: Avoid
List<Option<A>>
unless you really need it, just remove the elements if you don't need them, option makes this much weirder. Also use maps here if possible! The right data-structure at the right time makes things much easier. (Edit: Nvm I see that the list can contain the same thing multiple times) Now to the functionality itself: I get the confusion with the mutable list, quite problematic that you need structural changes inside a map. But I'd invert this: Instead of mapping over
testList
, why not fold over
toBeRemoved
? I'll write something up in a sec, dunno if my idea will work
btw you code does not typecheck because in actual
testList
is expected to be
List<Pair>
and not
List<Option<Pair>>
g
Yeah apologies, in the solution I removed
Option
to keep it concise. However it's necessary as empty spaces need to be represented too
j
About to ask that 🙂
Btw is there a reason for the for loop? Map keys are unique aren't they? Hence the if clause should only ever trigger once
kotlin makes avoiding mutable datastructures really annoying. Since I am reading Okasaki's book about purely functional data-structures atm I might implement some of those sooner or later... Let's ignore the mutable map for now and just keep it as it makes this much easier to deal with...
it's also local mutability so likely fine if the map is not used anywhere
Copy code
kotlin
val actual1 = testList.foldLeft(toBeRemoved.toMap() to emptyList<Option<Pair<String, Int>>>()) { (tbR, xs), x ->
  x.fold({ tbR to xs + x }, { (k, v) ->
    if (tbR.containsKey(k)) tbR[k]!!.let { value ->
      val rem = if (value > v) v else value
      val newTbR = tbR.mapValues { (key, v) -> if (k == key) v - rem else v }
      if (v - rem <= 0) newTbR to xs + None
      else newTbR to xs + (k to v - rem).some()
    } else tbR to xs + x
  })
}.second
Without mutability, could be cleaner with
traverse
over
State<Map<String, Int>>
With mutability we'll get very close to what you wrote before, just the for loop confuses me because keys in maps are unique
Idea here is to change the list while also retaining some state in the accumulator, instead of mutable state in global scope
I'll write the example with traverse and state as well, but that will likely be ugly in kotlin
g
I was thinking you could filter + map the amounts to be removed for each
toBeRemoved
id e.g
arrayOf(1, 1)
and then map the original list and subtract using the index. That way the only mutation is of a local variable to keep track of how much is left to be removed. Thoughts?
Doesn't sound very efficient though.
j
Yay network at home died, so I am typing this from my phone from now on... I don't like dealing with indices (so easy to mess up) , also that implies multiple passes over what seems to be the longer list. Taking a step back:, generally the behavior is a stateful map/traverse over testlist with toberemoved as state. The fold above and an implementation with state and traverse accomplish this without mutable state, but tbh I think your solution is also decent since the mutable state is contained to only that function. Except for one part and that is the for loop, the conditional in there will only ever be true once for any given value of x so what is the point of a loop here? That also obsoleted the var newAmount
Anyway its a bit late for me and with the network not working atm I'll go to sleep and maybe post some other ideas tomorrow, although I do think that either solution (the initial example without the for loop and with a local mutable map) or the left fold I posted is fine.
g
Likewise; late and on mobile. I will examine your sample more closely tomorrow, thank you for that. Ah yes that loop is completely useless.