Как заменить значение в одной карте значением другой карты на основе сопоставления с образцом? - PullRequest
2 голосов
/ 29 июня 2019

У меня есть карта, в которой есть несколько пар ключ-значение, и я хотел бы получить возможность просмотреть эти пары и попытаться сопоставить ключи со значением другой карты.Если есть совпадение, значения заменяются друг на друга.Другими словами, если есть совпадение, значение второй карты заменяется значением первой карты.Если совпадения нет, оно не включается в результат.

Я пытался выяснить логику, используя функцию scala .map, но я новичок в scala и не могу понять это.

Например, у меня есть следующие две карты scala [String, String]:

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

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

Я бы хотел получить какой-нибудь способ получить следующий результат:

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

Примечание: «baz» не был включен, потому что он не соответствовал чему-либо на карте поиска

Ответы [ 2 ]

4 голосов
/ 29 июня 2019

A for понимание может очистить это.

val result = for {
               (k,ev) <- entriesMap
               lv     <- lookupMap.get(ev)
             } yield (k,lv)
//result: Map[String,String] = Map(foo -> banana, bar -> apple)
3 голосов
/ 29 июня 2019

Давайте разберем вашу проблему более простыми шагами.

  1. Отфильтровать все пары в entriesMap, значение которых не существует в качестве ключа в lookupMap.
  2. Сопоставьте оставшиеся пары, чтобы изменить значение для lookupMap, связанного с исходным значением.

Таким образом, вы можете написать следующее:

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

Однако каждый раз, когда вы хотите filter, а затем map, вы всегда можете использовать collect (который будет выполнять ту же работу, но только за одну итерацию) .
Таким образом, вы можете написать это:

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

Теперь, одна "проблема" с приведенным выше кодом заключается в том, что он использует небезопасно apply над картой , что вызовет исключение, если Их ключ не существует. Обычно следует использовать метод get, который будет возвращать значение, заключенное в Option , которое будет None, если ключ не существовал.

В этом случае доступ небезопасен, потому что мы проверяем, существует ли ключ раньше.
В любом случае, программу можно переосмыслить следующим образом:

  1. Сопоставьте значения entriesMap, пытаясь получить их связанное значение в lookupMap.
  2. Отфильтруйте пары, значения которых теперь равны None, и разверните Somes.

Код будет следующим:

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.

Наконец, вместо непосредственного использования функций более высокого порядка можно использовать для понимания .
Таким образом, этот код (почти такой же, как код из ответа jwvh) :

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.
...