Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Idiomatic way to update value in a Map based on previous value

Let's say I store bank accounts information in an immutable Map:

val m = Map("Mark" -> 100, "Jonathan" -> 350, "Bob" -> 65) 

and I want to withdraw, say, $50 from Mark's account. I can do it as follows:

val m2 = m + ("Mark" -> (m("Mark") - 50)) 

But this code seems ugly to me. Is there better way to write this?

like image 638
ffriend Avatar asked Jan 25 '12 13:01

ffriend


People also ask

How to update values in HashMap?

You can use computeIfPresent method and supply it a mapping function, which will be called to compute a new value based on existing one. For example, Map<String, Integer> words = new HashMap<>(); words. put("hello", 3); words.

How to update value for a key in HashMap?

Example 2: Update value of HashMap using computeIfPresent() In the above example, we have recomputed the value of the key Second using the computeIfPresent() method. To learn more, visit HashMap computeIfPresent(). Here, we have used the lambda expression as the method argument to the method.

How to assign value of key in HashMap java?

HashMap put() Method in Java HashMap. put() method of HashMap is used to insert a mapping into a map. This means we can insert a specific key and the value it is mapping to into a particular map. If an existing key is passed then the previous value gets replaced by the new value.

How do I add values to a map in Scala?

Adding new key-value pair We can insert new key-value pairs in a mutable map using += operator followed by new pairs to be added or updated.


2 Answers

There's no adjust in the Map API, unfortunately. I've sometimes used a function like the following (modeled on Haskell's Data.Map.adjust, with a different order of arguments):

def adjust[A, B](m: Map[A, B], k: A)(f: B => B) = m.updated(k, f(m(k))) 

Now adjust(m, "Mark")(_ - 50) does what you want. You could also use the pimp-my-library pattern to get the more natural m.adjust("Mark")(_ - 50) syntax, if you really wanted something cleaner.

(Note that the short version above throws an exception if k isn't in the map, which is different from the Haskell behavior and probably something you'd want to fix in real code.)

like image 112
Travis Brown Avatar answered Sep 28 '22 04:09

Travis Brown


This could be done with lenses. The very idea of a lens is to be able to zoom in on a particular part of an immutable structure, and be able to 1) retrieve the smaller part from a larger structure, or 2) create a new larger structure with a modified smaller part. In this case, what you desire is #2.

Firstly, a simple implementation of Lens, stolen from this answer, stolen from scalaz:

case class Lens[A,B](get: A => B, set: (A,B) => A) extends Function1[A,B] with Immutable {   def apply(whole: A): B   = get(whole)   def updated(whole: A, part: B): A = set(whole, part) // like on immutable maps   def mod(a: A)(f: B => B) = set(a, f(this(a)))   def compose[C](that: Lens[C,A]) = Lens[C,B](     c => this(that(c)),     (c, b) => that.mod(c)(set(_, b))   )   def andThen[C](that: Lens[B,C]) = that compose this } 

Next, a smart constructor to create a lens from "larger structure" Map[A,B] to "smaller part" Option[B]. We indicate which "smaller part" we want to look at by providing a particular key. (Inspired by what I remember from Edward Kmett's presentation on Lenses in Scala):

def containsKey[A,B](k: A) = Lens[Map[A,B], Option[B]](   get = (m:Map[A,B]) => m.get(k),   set = (m:Map[A,B], opt: Option[B]) => opt match {     case None => m - k     case Some(v) => m + (k -> v)   } ) 

Now your code can be written:

val m2 = containsKey("Mark").mod(m)(_.map(_ - 50)) 

n.b. I actually changed mod from the answer I stole it from so that it takes its inputs curried. This helps to avoid extra type annotations. Also notice _.map, because remember, our lens is from Map[A,B] to Option[B]. This means the map will be unchanged if it does not contain the key "Mark". Otherwise, this solution ends up being very similar to the adjust solution presented by Travis.

like image 25
Dan Burton Avatar answered Sep 28 '22 03:09

Dan Burton