scala map - кой е най-добрият начин да опитате различен ключ, ако ключът не съществува

Имайки дадена карта в 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]“

И така, какъв би бил най-добрият начин да направите това?


person lznt    schedule 19.09.2019    source източник
comment
@LuisMiguelMejíaSuárez, благодаря! можеш ли да го поставиш като отговор, за да мога да го маркирам?   -  person lznt    schedule 20.09.2019


Отговори (1)


Имате добра интуиция за това, което искате да направите, което е:
"тествайте един ключ, ако не съществува, проверете втория".

Освен това сте прави за типа на връщането, защото не сте посочили никакъв ключ по подразбиране и няма гаранция, че някой от ключовете трябва да съществува.
Но проблемът ви е, че scores.getOrElse("NotAKey", scores.get("NotAKeyAgain")) ще върне Всеки.
Защо? Тъй като getOrElse връща LUB (най-малката горна граница) и на двете алтернативи. В този случай по подразбиране е Option[Int], но типът на успешното извличане на първия ключ е Int.

Това, от което наистина се нуждаете, е начин да съставите две Опции, където втората се използва в случай, че първата не съществува. И точно това е orElse прави.

Можете да създадете някои методи за разширение върху Карта, за да направите използването по-лесно.

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)
person Luis Miguel Mejía Suárez    schedule 19.09.2019