Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Filter Map by key set

Tags:

scala

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.

like image 424
Timo Westkämper Avatar asked Jan 27 '12 09:01

Timo Westkämper


People also ask

How do I filter a stream Map?

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.

Can we use filter and Map together in Java 8?

With Java 8, you can convert a Map. entrySet() into a stream , follow by a filter() and collect() it.


2 Answers

Answering the Question

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) 

Predicates

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) } 
like image 55
oxbow_lakes Avatar answered Oct 07 '22 19:10

oxbow_lakes


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.

like image 36
missingfaktor Avatar answered Oct 07 '22 19:10

missingfaktor