Карта фильтра по набору ключей
Есть ли ярлык для фильтрации карты, содержащей только записи, в которых ключ содержится в заданном наборе?
Вот пример кода
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")