карта скалы - как лучше всего попробовать другой ключ, если ключ не существует - PullRequest
0 голосов
/ 20 сентября 2019

Учитывая карту в Scala, я хочу попробовать первый ключ, если не найден, попробовать другой ключ, если не найден снова, вернуть None.Следующее работает должным образом:

val scores: Map[String, Int] = Map("Alice" -> 10, "Bob" -> 3)
val singleGet: Option[Int] = scores.get("NotAKey")
println(singleGet) // None
val doubleGet = scores.getOrElse("NotAKey", scores.get("NotAKeyAgain")) // works ok if no type
println(doubleGet) // None

Но если я добавлю тип для doubleGet, он выдаст ошибку:

val doubleGet: Option[Int] = scores.getOrElse("NotAKey", scores.get("NotAKeyAgain")) // ERROR

"Выражение типа Any не соответствует ожидаемому типу Option [Int] "

Итак, как лучше всего это сделать?

1 Ответ

2 голосов
/ 20 сентября 2019

У вас есть хорошая интуиция в том, что вы хотите сделать, а именно:
"проверить один ключ, если его не существует, то проверить второй" .

Кроме того, вы правильно указали тип возвращаемого значения, потому что вы не указали никаких значений по умолчанию, и нет гарантии, что любой из этих ключей должен существовать.
Но ваша проблема в том, что scores.getOrElse("NotAKey", scores.get("NotAKeyAgain")) вернет Любой .
Почему?Потому что getOrElse возвращает LUB (наименьшая верхняя граница) обеих альтернатив.В этом случае значение по умолчанию - Option[Int], но тип успешного извлечения первого ключа - Int.

. Что вам действительно нужно, так это способ составить две опции ,где второй используется в случае, если первый не существует.И это именно то, что orElse делает.

Вы можете создать несколько методов расширения поверх Map , чтобы упростить использование.

implicit class MapOps[K, V](private val map: Map[K, V]) extends AnyVal {
  def doubleGet(key1: K, key2: K): Option[V] =
    map.get(key1) orElse map.get(key2)

  def doubleGetOrElse[V1 >: V](key1: K, key2: K)(default: => V1): V1 =
    (map.get(key1) orElse map.get(key2)).getOrElse(default)

  def multiGet(keys: K*): Option[V] =
    keys.iterator.map(key => map.get(key)).foldLeft(Option.empty[V])(_ orElse _)

  def multiGetOrElse[V1 >: V](keys: K*)(default: => V1): V1 =
    keys.iterator.map(key => map.get(key)).foldLeft(Option.empty[V])(_ orElse _).getOrElse(default)
}

val scores: Map[String, Int] = Map("Alice" -> 10, "Bob" -> 3)
scores.doubleGet("A", "B") // res: Option[Int] = None
scores.multiGet("A", "Alice", "B") // res: Option[Int] = Some(10)
...