How to merge Scala sequential collections (List, Vector, ArrayBuffer, Array, Seq)

This is an excerpt from the Scala Cookbook (partially modified for the internet). This is Recipe 10.22, “How to Merge Scala Sequential Collections”

Back to top

Problem

You want to join two sequences into one sequence, either keeping all of the original elements, finding the elements that are common to both collections, or finding the difference between the two sequences.

Back to top

Solution

There are a variety of solutions to this problem, depending on your needs:

  • Use the ++= method to merge a sequence into a mutable sequence
  • Use the ++ method to merge two mutable or immutable sequences
  • Use collection methods like union, diff, and intersect
Back to top

The ++= method

Use the ++= method to merge a sequence (any TraversableOnce) into a mutable collection like an ArrayBuffer:

scala> val a = collection.mutable.ArrayBuffer(1,2,3)
a: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(1, 2, 3)

scala> a ++= Seq(4,5,6)
res0: a.type = ArrayBuffer(1, 2, 3, 4, 5, 6)
Back to top

The ++ method

Use the ++ method to merge two mutable or immutable collections while assigning the result to a new variable:

scala> val a = Array(1,2,3)
a: Array[Int] = Array(1, 2, 3)

scala> val b = Array(4,5,6)
b: Array[Int] = Array(4, 5, 6)

scala> val c = a ++ b
c: Array[Int] = Array(1, 2, 3, 4, 5, 6)
Back to top

union, intersect

You can also use methods like union and intersect to combine sequences to create a resulting sequence:

scala> val a = Array(1,2,3,4,5)
a: Array[Int] = Array(1, 2, 3, 4, 5)

scala> val b = Array(4,5,6,7,8)
b: Array[Int] = Array(4, 5, 6, 7, 8)

// elements that are in both collections
scala> val c = a.intersect(b)
c: Array[Int] = Array(4, 5)

// all elements from both collections
scala> val c = a.union(b)
c: Array[Int] = Array(1, 2, 3, 4, 5, 4, 5, 6, 7, 8)

// distinct elements from both collections
scala> val c = a.union(b).distinct
c: Array[Int] = Array(1, 2, 3, 4, 5, 6, 7, 8)
Back to top

diff

The diff method results depend on which sequence it’s called on:

scala> val c = a diff b
c: Array[Int] = Array(1, 2, 3)

scala> val c = b diff a
c: Array[Int] = Array(6, 7, 8)

The Scaladoc for the diff method states that it returns, “a new list which contains all elements of this list except some of occurrences of elements that also appear in that. If an element value x appears n times in that, then the first n occurrences of x will not form part of the result, but any following occurrences will.”

The objects that correspond to most collections also have a concat method:

scala> Array.concat(a, b)
res0: Array[Int] = Array(1, 2, 3, 4, 4, 5, 6, 7)
Back to top

::: with List

If you happen to be working with a List, the ::: method prepends the elements of one list to another list:

scala> val a = List(1,2,3,4)
a: List[Int] = List(1, 2, 3, 4)

scala> val b = List(4,5,6,7)
b: List[Int] = List(4, 5, 6, 7)

scala> val c = a ::: b
c: List[Int] = List(1, 2, 3, 4, 4, 5, 6, 7)
Back to top

Discussion

You can also use the diff method to get the relative complement of two sets. The relative complement of a set A with respect to a set B is the set of elements in B that are not in A.

On a recent project, I needed to find the elements in one list that weren’t in another list. I did this by converting the lists to sets, and then using the diff method to compare the two sets. For instance, given these two arrays:

val a = Array(1,2,3,11,4,12,4,5)
val b = Array(6,7,4,5)

you can find the relative complement of each array by first converting them to sets (to eliminate the duplicate values), and then comparing them with the diff method:

// the elements in a that are not in b
scala> val c = a.toSet diff b.toSet
c: scala.collection.immutable.Set[Int] = Set(1, 2, 12, 3, 11)

// the elements in b that are not in a
scala> val d = b.toSet diff a.toSet
d: scala.collection.immutable.Set[Int] = Set(6, 7)

If desired, you can then sum those results to get the list of elements that are either in the first set or the second set, but not both sets:

scala> val complement = c ++ d
complement: scala.collection.immutable.Set[Int] = Set(1, 6, 2, 12, 7, 3, 11)

This works because diff returns a set that contains the elements in the current set (this) that are not in the other set (that).

You can also use the -- method to get the same result:

scala> val c = a.toSet -- b.toSet
c: scala.collection.immutable.Set[Int] = Set(1, 2, 12, 3, 11)

scala> val d = b.toSet -- a.toSet
d: scala.collection.immutable.Set[Int] = Set(6, 7)

Subtracting the intersection of the two sets also yields the same result:

scala> val i = a.intersect(b)
i: Array[Int] = Array(4, 5)

scala> val c = a.toSet -- i.toSet
c: scala.collection.immutable.Set[Int] = Set(1, 2, 12, 3, 11)

scala> val d = b.toSet -- i.toSet
d: scala.collection.immutable.Set[Int] = Set(6, 7)
Back to top

The Scala Cookbook

This tutorial is sponsored by the Scala Cookbook, which I wrote for O’Reilly:

You can find the Scala Cookbook at these locations:

Back to top

Add new comment

The content of this field is kept private and will not be shown publicly.

Anonymous format

  • Allowed HTML tags: <em> <strong> <cite> <code> <ul type> <ol start type> <li> <pre>
  • Lines and paragraphs break automatically.
By submitting this form, you accept the Mollom privacy policy.