What is an idiomatic Scala way to "remove" one element from an immutable List?

I have a List, which may contain elements that will compare as equal. I would like a similar List, but with one element removed. So from (A, B, C, B, D) I would like to be able to "remove" just one B to get e.g. (A, C, B, D). The order of the elements in the result does not matter.

I have working code, written in a Lisp-inspired way in Scala. Is there a more idiomatic way to do this?

The context is a card game where two decks of standard cards are in play, so there may be duplicate cards but still played one at a time.

def removeOne(c: Card, left: List[Card], right: List[Card]): List[Card] = {
  if (Nil == right) {
    return left
  }
  if (c == right.head) {
    return left ::: right.tail
  }
  return removeOne(c, right.head :: left, right.tail)
}

def removeCard(c: Card, cards: List[Card]): List[Card] = {
  return removeOne(c, Nil, cards)
}

I haven't seen this possibility in the answers above, so:

scala> def remove(num: Int, list: List[Int]) = list diff List(num)
remove: (num: Int,list: List[Int])List[Int]

scala> remove(2,List(1,2,3,4,5))
res2: List[Int] = List(1, 3, 4, 5)

Edit:

scala> remove(2,List(2,2,2))
res0: List[Int] = List(2, 2)

Like a charm :-).


You could use the filterNot method.

val data = "test"
list = List("this", "is", "a", "test")
list.filterNot(elm => elm == data)

You could try this:

scala> val (left,right) = List(1,2,3,2,4).span(_ != 2)
left: List[Int] = List(1)
right: List[Int] = List(2, 3, 2, 4)

scala> left ::: right.tail                            
res7: List[Int] = List(1, 3, 2, 4)

And as method:

def removeInt(i: Int, li: List[Int]) = {
   val (left, right) = li.span(_ != i)
   left ::: right.drop(1)
}

Unfortunately, the collections hierarchy got itself into a bit of a mess with - on List. For ArrayBuffer it works just like you might hope:

scala> collection.mutable.ArrayBuffer(1,2,3,2,4) - 2
res0: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(1, 3, 2, 4)

but, sadly, List ended up with a filterNot-style implementation and thus does the "wrong thing" and throws a deprecation warning at you (sensible enough, since it is actually filterNoting):

scala> List(1,2,3,2,4) - 2                          
warning: there were deprecation warnings; re-run with -deprecation for details
res1: List[Int] = List(1, 3, 4)

So arguably the easiest thing to do is convert List into a collection that does this right, and then convert back again:

import collection.mutable.ArrayBuffer._
scala> ((ArrayBuffer() ++ List(1,2,3,2,4)) - 2).toList
res2: List[Int] = List(1, 3, 2, 4)

Alternatively, you could keep the logic of the code you've got but make the style more idiomatic:

def removeInt(i: Int, li: List[Int]) = {
  def removeOne(i: Int, left: List[Int], right: List[Int]): List[Int] = right match {
    case r :: rest =>
      if (r == i) left.reverse ::: rest
      else removeOne(i, r :: left, rest)
    case Nil => left.reverse
  }
  removeOne(i, Nil, li)
}

scala> removeInt(2, List(1,2,3,2,4))
res3: List[Int] = List(1, 3, 2, 4)