Saqib Ali
Saqib Ali

Reputation: 4428

Scala Collection filtering on multiple items

I have the following recursive function to remove items from a list (zooResidents: List[(String, Int)]) that are contained in another list (pets: List[String]). It works, but is very slow. What is the Scala way of doing this?

val pets = List("cat", "dog")

val zooResidents = List(("cat", 4), ("lion", 2), ("tiger", 3), ("dog", 2) 

def removePets(zooResidents: List[(String, Int)], pets: List[String]): List[(String, Int)] =  {
  if (pets.isEmpty) zooResidents
  else removePets(zooResidents.filterNot(_._1.contains(pets.head)), pets.tail)
}

removePets(zooResidents, pets)      //> res2: List[(String, Int)] = List((lion,2), (tiger,3))

Upvotes: 3

Views: 1096

Answers (3)

RAGHHURAAMM
RAGHHURAAMM

Reputation: 1099

val zooResidents = List(("cat", 4), ("lion", 2), ("tiger", 3), ("dog", 2)
val pets = List("cat", "dog")

Try this one-liner:

zooResidents.filter(x=>pets.toSet.contains(x._1)==false)

scala> zooResidents.filter(x=>pets.toSet.contains(x._1)==false)
res167: List[(String, Int)] = List((lion,2), (tiger,3))

pets changed to be a Set for performance.

This takes zooResidents List and checks for existence of each string in pets also and returns only those elements of zooResidents which are not in pets the ==false will do this.

Upvotes: 1

Łukasz
Łukasz

Reputation: 8663

This is what I would call scala way

@ zooResidents filterNot { case (resident, _) => pets contains resident } 
res6: List[(String, Int)] = List(("lion", 2), ("tiger", 3))

For better performance, pets should be a Set.

filterNot takes only those elements that do not fulfill the predicate, i.e. those for which the given function returns false. So we want to take all those elements for which pets does not contain first element of the tuple.

You can use pattern matching instead of regular function to destructure tuple as I did this

{ case (resident, _) => pets contains resident } 

this is equvalent to

(residentTuple => pets.contains(residentTuple._1))

Upvotes: 2

Sleiman Jneidi
Sleiman Jneidi

Reputation: 23329

Note that List#contains is linear as it has to scan the entire list, I would recommend you using a data-structure with constant-time lookup like Set

val petSet = pets.toSet
val filter = zooResidents.filterNot(element => petSet.contains(element._1))

Upvotes: 5

Related Questions