Карта фильтра по набору ключей

Есть ли ярлык для фильтрации карты, содержащей только записи, в которых ключ содержится в заданном наборе?

Вот пример кода

scala> val map = Map("1"->1, "2"->2, "3"->3)
map: scala.collection.immutable.Map[java.lang.String,Int] = Map(1 -> 1, 2 -> 2, 3 -> 3)

scala> map.filterKeys(Set("1","2").contains)
res0: scala.collection.immutable.Map[java.lang.String,Int] = Map(1 -> 1, 2 -> 2)

Я ищу что-то меньшее, чем это.

Ответ 1

Отвечая на вопрос

Вы можете воспользоваться тем, что a Set[A] является предикатом; т.е. A => Boolean

map filterKeys set

Здесь он работает:

scala> val map = Map("1" -> 1, "2" -> 2, "3" -> 3)
map: scala.collection.immutable.Map[java.lang.String,Int] = Map(1 -> 1, 2 -> 2, 3 -> 3)

scala> val set = Set("1", "2")
set: scala.collection.immutable.Set[java.lang.String] = Set(1, 2)

scala> map filterKeys set
res0: scala.collection.immutable.Map[java.lang.String,Int] = Map(1 -> 1, 2 -> 2)

Или, если вы предпочитаете:

scala> map filterKeys Set("1", "2")
res1: scala.collection.immutable.Map[java.lang.String,Int] = Map(1 -> 1, 2 -> 2)

Предикаты

На самом деле очень полезно иметь некоторую оболочку вокруг предиката. Например:

scala> class PredicateW[A](self: A => Boolean) {
   | def and(other: A => Boolean): A => Boolean = a => self(a) && other(a)
   | def or(other: A => Boolean): A => Boolean = a => self(a) || other(a)
   | def unary_! : A => Boolean = a => !self(a)
   | }
defined class PredicateW

И неявное преобразование:

scala> implicit def Predicate_Is_PredicateW[A](p: A => Boolean) = new PredicateW(p)
Predicate_Is_PredicateW: [A](p: A => Boolean)PredicateW[A]

И затем вы можете использовать его:

scala> map filterKeys (Set("1", "2") and Set("2", "3"))
res2: scala.collection.immutable.Map[java.lang.String,Int] = Map(2 -> 2)

scala> map filterKeys (Set("1", "2") or Set("2", "3"))
res3: scala.collection.immutable.Map[java.lang.String,Int] = Map(1 -> 1, 2 -> 2, 3 -> 3)

scala> map filterKeys !Set("2", "3")
res4: scala.collection.immutable.Map[java.lang.String,Int] = Map(1 -> 1)

Это может быть расширено до xor, nand и т.д. и т.д., и если вы включите символический юникод, можете сделать для удивительно читаемого кода:

val mustReport = trades filter (uncoveredShort ∨ exceedsDollarMax)

val european = { 
  val Europe = (_ : Market).exchange.country.region == Region.EU
  trades filter (_.market ∈: Europe)
}

Ответ 2

Тангенциальный наконечник, если вы будете следовать идее PredicateW в ответе @oxbow_lakes:

В функциональном программировании вместо определения специальных функций мы стремимся к более обобщенным и составным абстракциям. Для этого конкретного случая Applicative соответствует счету.

Set сами являются функциями, а экземпляр Applicative для [B]Function1[A, B] позволяет нам поднять функции в контекст. Другими словами, вы можете поднять функции (Boolean, Boolean) => Boolean (например, ||, && и т.д.) До (A => Boolean, A => Boolean) => (A => Boolean). (Здесь вы можете найти отличное объяснение этой концепции подъема.)

Однако сама структура данных Set имеет экземпляр Applicative, который будет использоваться в качестве экземпляра [B]Applicative[A => B]. Чтобы этого не произошло, нам нужно будет прямо сказать компилятору рассматривать данный набор как функцию. Мы определяем для этого следующее обогащение:

scala> implicit def setAsFunction[A](set: Set[A]) = new {
     |   def f: A => Boolean = set
     | }
setAsFunction: [A](set: Set[A])java.lang.Object{def f: A => Boolean}

scala> Set(3, 4, 2).f
res144: Int => Boolean = Set(3, 4, 2)

И теперь примените эту доброту Applicative.

scala> val map = Map("1" -> 1, "2" -> 2, "3" -> 3)
map: scala.collection.immutable.Map[java.lang.String,Int] = Map(1 -> 1, 2 -> 2, 3 -> 3)

scala> map filterKeys ((Set("1", "2").f |@| Set("2", "3").f)(_ && _))
res150: scala.collection.immutable.Map[java.lang.String,Int] = Map(2 -> 2)

scala> map filterKeys ((Set("1", "2").f |@| Set("2", "3").f)(_ || _))
res151: scala.collection.immutable.Map[java.lang.String,Int] = Map(1 -> 1, 2 -> 2, 3 -> 3)

scala> map filterKeys (Set("2", "3").f map (!_))
res152: scala.collection.immutable.Map[java.lang.String,Int] = Map(1 -> 1)

Примечание. Все вышеперечисленное требует Scalaz.

Ответ 3

Извините, не прямой ответ на ваш вопрос, но если вы знаете, какие ключи вы хотите удалить (вместо которых вы хотите сохранить), вы можете сделать это:

map -- Set("3")