Is there a shortcut to filter a Map keeping only the entries where the key is contained in a given Set?
Here is some example code
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)
I am searching for something shorter than this.
Since our filter condition requires an int variable we first need to convert Stream of String to Stream of Integer. That's why we called the map() function first. Once we have the Stream of Integer, we can apply maths to find the even numbers. We passed that condition to filter method.
With Java 8, you can convert a Map. entrySet() into a stream , follow by a filter() and collect() it.
You can take advantage of the fact that a Set[A]
is a predicate; i.e. A => Boolean
map filterKeys set
Here it is at work:
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)
Or if you prefer:
scala> map filterKeys Set("1", "2") res1: scala.collection.immutable.Map[java.lang.String,Int] = Map(1 -> 1, 2 -> 2)
It's actually really useful to have some wrapper around a predicate. Like so:
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
And an implicit conversion:
scala> implicit def Predicate_Is_PredicateW[A](p: A => Boolean) = new PredicateW(p) Predicate_Is_PredicateW: [A](p: A => Boolean)PredicateW[A]
And then you can use it:
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)
This can be extended to xor
, nand
etc etc and if you include symbolic unicode can make for amazingly readable code:
val mustReport = trades filter (uncoveredShort ∨ exceedsDollarMax) val european = { val Europe = (_ : Market).exchange.country.region == Region.EU trades filter (_.market ∈: Europe) }
A tangential tip, in case you are going to follow the PredicateW
idea in @oxbow_lakes' answer:
In functional programming, instead of defining ad hoc functions, we aim for more generalized and composable abstractions. For this particular case, Applicative
fits the bill.
Set
themselves are functions, and the Applicative
instance for [B]Function1[A, B]
lets us lift functions to context. In other words, you can lift functions of type (Boolean, Boolean) => Boolean
(such as ||
, &&
etc.) to (A => Boolean, A => Boolean) => (A => Boolean)
. (Here you can find a great explanation on this concept of lifting.)
However the data structure Set
itself has an Applicative
instance available, which will be favored over [B]Applicative[A => B]
instance. To prevent that, we will have to explicitly tell the compiler to treat the given set as a function. We define a following enrichment for that:
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)
And now put this Applicative
goodness into use.
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)
Note: All of the above requires Scalaz.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With