1. Introduction

Scala offers a comprehensive set of collections with powerful operations for data manipulation. Sequential collections like List, ListBuffer, and ArrayBuffer are particularly useful for handling ordered data. Removing elements from these collections is a common task, and Scala provides various methods to achieve this.

In this tutorial, we’ll explore how to remove elements from both mutable and immutable sequential collections, focusing on techniques for removing both the first match and all matches of a given element.

2. Remove the First Match

In this section, we’ll examine ways to remove the first match of a given element from sequential collections in Scala. We’ll explore this with both immutable and mutable collections.

2.1. Using span()

We can use the span() function on a collection to split it based on a predicate. Let’s look at how we can use this to remove the first match:

val input = List("abc", "def", "xyz", "abc")
val ele = "abc"
val (part1, part2) = input.span(_ != ele)
val result = part1 ++ part2.drop(1)
result shouldBe List("def", "xyz", "abc")

The span() function splits a collection into a tuple of two parts. The first part contains all elements up to the first element that matches the predicate, while the second part contains the rest of the list, starting with the matching element. If a match is found, it’ll be the first element in the second part of the tuple. To remove this element, we can drop the first element from the second part and combine the two parts to obtain a collection with the removed element. If there is no element, the second part will be empty, and the drop() function will return an empty list.

2.2. Using Pattern Matching

Pattern matching is a powerful concept in Scala that can be used in many scenarios to manipulate collections. We can use it to remove the first match of an element from the collection:

val input = List("abc", "def", "xyz", "abc")
val ele = "abc"
val result = input match {
  case `ele` :: sub => sub
  case sub => sub
}
result shouldBe List("def", "xyz", "abc")

This code checks if the first element of the list matches the element and removes it if it does; otherwise, it leaves the list unchanged. Notice the usage of backtick(`) to surround the variable in the pattern match. When we surround a variable or value with backticks in a pattern, it instructs the compiler to match against the value of that variable rather than binding the variable to a new value.

2.3. Using -=

In the previous sections, we used immutable collections. Now, let’s check how to remove the first match on a mutable collection. Let’s use ListBuffer for our example:

val buffer = ListBuffer[String]("abc", "def", "xyz", "def")
buffer -= "def"
buffer shouldBe ListBuffer[String]("abc", "xyz", "def")

We used the operation -= to remove the first matching element. Alternatively, we can use the subtractOne() function, which works in the same way.

3. Remove All Matches

Let’s look at different ways to remove all the matches from a collection.

3.1. Using filter()

We can use the filter() function to remove the matching elements from a collection:

val list = List("abc", "def", "xyz", "def")
val filteredList = list.filter(_ != "def")
filteredList shouldBe List("abc", "xyz")
list.filterNot(_ == "def") shouldBe List("abc", "xyz")

The filter() method removed the matching elements and returned a new list. Alternatively, we can use the filterNot() function without using the negative condition to achieve the same result.

We can apply the same function to ListBuffer as well. However, the filter() and filterNot() methods return a new ListBuffer with the filtered elements rather than modifying the existing ListBuffer.

3.2. Using collect()

Another method to achieve the same result is by using the collect() function:

val input = List("abc", "def", "xyz", "def")
val ele = "def"
val result = input.collect {
  case e if e != ele => e
}
result shouldBe List("abc", "xyz")

We used the collect() function with the required partial function to remove the matching elements.

3.3. Using partition()

We can partition a collection based on a predicate. We can use this to remove the matching element from the collection:

val input = List("abc", "def", "xyz", "def")
val ele = "def"
val (result, _) = input.partition(_ != ele)
result shouldBe List("abc", "xyz")

The partition() function returns a tuple of two elements. The first element contains the elements that match the predicate, while the second contains those that don’t. In this case, the first part contains the list without the input element.

3.4. Using filterInPlace()

Mutable collections such as ListBuffer and ArrayBuffer provide another way to remove the elements using the filterInPlace() function:

val buffer = ListBuffer[String]("abc", "def", "xyz", "def")
buffer.filterInPlace(_ != "def")
buffer shouldBe ListBuffer[String]("abc", "xyz")

It works similarly to the filter() function. However, instead of returning a new collection, it removes the matching elements from the existing collection.

3.5. Using foldLeft()

Yet another approach to achieve this functionality is by using the foldLeft() function:

val input = List("abc", "def", "xyz", "def")
val ele = "def"
val result = input.foldLeft(List.empty[String])((acc,i) => if(i != ele) acc :+ i else acc)
result shouldBe List("abc", "xyz")

This code uses foldLeft() to create a new list by iterating through the original list and appending elements to the accumulator only if they are not equal to the given element, effectively removing all occurrences from the list.

4. Conclusion

In this article, we explored various methods for removing an element from a list, covering both single and multiple occurrences. We also applied these techniques to both immutable and mutable collections.

As always, the sample code we used in this article is available over on GitHub.