jjaguirre394
jjaguirre394

Reputation: 191

How to replace a value in one map with the value of another map, based on a pattern match?

I have a map that has a few key value pairs, and I would like a way to go through those pairs and attempt to match the keys with the value of another map. If there's a match the values are substituted for each other. In other words, if there's a match value of the second map is substituted for the value of the first map. If there is no match, it is not included in the result.

I've tried figuring out the logic use the scala .map function but I'm new to scala and can't quite figure it out.

For example, I have the following two scala Map[String, String]:

val lookupMap = Map("aaa" -> "apple", "bbb" -> "orange", "ccc" -> "banana")

val entriesMap = Map("foo" -> "ccc", "bar"-> "aaa", "baz" -> "zzz") 

I would like some way to get the following result:

val result = Map("foo" -> "banana", "bar" -> "apple")

Note: "baz" was not included because it did not match to anything in the lookup Map.

Upvotes: 2

Views: 1725

Answers (2)

Lets break down your problem in simpler steps.

  1. Filter out all pairs on the entriesMap whose value does not exists as a key in the lookupMap.
  2. Map the remaining pairs to change the value for the value on the lookupMap associated with the original value.

Thus, you can write the following:

val result =
  entriesMap
    .filter { case (_, value) => lookupMap.contains(key = value) }
    .map { case (key, value) => key -> lookupMap(value) }

However, every time that you want to filter and then map, you can always use collect (which will do the same job, but in just one iteration).
Thus, you can write this:

val result = entriesMap.collect {
  case (key, value) if lookupMap.contains(key = value) => key -> lookupMap(value)
}

Now, one "problem" with the above code is that it uses the unsafe apply over a Map, which will throw an exception if they key does not exists. Usually, one should use the get method, which would return the value wrapped on an Option, which will be a None if the key did not existed.

In this case, the access is not unsafe, because we are checking if the key exists before.
Anyways, one could rethink the program as:

  1. Map the values of the entriesMap by attempting to get their associated value on the lookupMap.
  2. Filter out the pairs where its values is now a None and unwrapping the Somes.

The code will be as follows:

val result =
  entriesMap
    .view // Only if you are in 2.13
    .mapValues(value => lookupMap.get(key = value))
    .collect { case (key, Some(value)) => key -> value }
    .toMap // This is necessary because mapValues returns a view, which is a lazy collection.
           // Other option would have been to use just map instead of mapValues.

Finally, instead of using higher order functions directly, one could use for comprehension.
Thus, this code (almost the same as the one from jwvh's answer):

val result =
  for {
    (key, value) <- entriesMap // For each key-value pair in entriesMap...
    newValue <- lookupMap.get(key = value) // And for each newValue associated with each original value...
  } yield key -> newValue // Yield the key-newValue pair.

Upvotes: 3

jwvh
jwvh

Reputation: 51271

A for comprehension can clean that up.

val result = for {
               (k,ev) <- entriesMap
               lv     <- lookupMap.get(ev)
             } yield (k,lv)
//result: Map[String,String] = Map(foo -> banana, bar -> apple)

Upvotes: 5

Related Questions