Фильтр карты по набору ключей - PullRequest
26 голосов
/ 27 января 2012

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

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

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)

Я ищу что-то более короткое, чем это.

Ответы [ 3 ]

50 голосов
/ 27 января 2012

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

Вы можете воспользоваться тем фактом, что 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)
}
9 голосов
/ 10 февраля 2012

Тангенциальный совет, если вы собираетесь следовать идее 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.

8 голосов
/ 27 января 2012

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

map -- Set("3")
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...