25

While working through converting some Java code over to Scala, I discovered while there is a contains method for Scala's Set, there isn't a containsAll method. Am I just missing the correct method name?

Here's a bit of code I worked up to fill in the gap so I could quickly get back to working. Is it sufficient, or am I missing some subtlety?

  def containsAll[A](set: Set[A], subset: Set[A]): Boolean =
    if (set.size >= subset.size)
      subset.forall(a => set.contains(a))
    else
      false
chaotic3quilibrium
  • 5,661
  • 8
  • 53
  • 86

4 Answers4

41

There is subsetOf, which tests whether or not the elements of a Set are contained within another Set. (Kind of the reverse in terms of the expression)

val set = Set(1,2,3,4)
val subset = Set(1,2)

scala> subset.subsetOf(set)
res0: Boolean = true

scala> set.subsetOf(subset)
res1: Boolean = false
Michael Zajac
  • 55,144
  • 7
  • 113
  • 138
  • 3
    Ah. Very nice. It's a bit counterintutive in that it is reversed from Java. But it certainly does what I need. Tysvm for your rapid response. – chaotic3quilibrium Feb 24 '15 at 20:45
8

In Scala, Set is equipped with set operations such as intersect, thus for instance

set.intersect(subset) == subset

conveys the semantics of containsAll, even that subsetOf as already mentioned proves the most succinct.

elm
  • 20,117
  • 14
  • 67
  • 113
  • Isn't this substantially less performant on sets of very large size (imagine millions of entries)? IOW, now there are two operations, performing the intersection and then performing the equivalence check. It seems a single pass through the first set testing for elements within the second set would be far preferable and far more performant. – chaotic3quilibrium Oct 25 '16 at 14:24
6

It's worth adding that you can make derived helper methods like containsAll available on Set[T] if you want, by using an implicit enriched class. You might also consider making a variadic overload:

implicit class RichSet[T](val x: Set[T]) extends AnyVal {
    def containsAll(y: Set[T]): Boolean = y.subsetOf(x)
    def containsAll(y: T*): Boolean = x.containsAll(y.toSet)
}

So then you can do:

Set(1, 2, 3).containsAll(Set(1, 2))

Or:

Set(1, 2, 3).containsAll(1, 2)
Ben Reich
  • 16,222
  • 2
  • 38
  • 59
  • Very nicely done! I will be definitely adding this to my library. – chaotic3quilibrium Feb 25 '15 at 03:51
  • What's the reasoning behind explicitly using 'extends AnyVal' as opposed to leaving that implied? – chaotic3quilibrium Feb 25 '15 at 16:18
  • 5
    It is more efficient. From the docs: "...it is possible to define a subclass of AnyVal called a user-defined value class which is treated specially by the compiler. Properly-defined user value classes provide a way to improve performance on user-defined types by avoiding object allocation at runtime, and by replacing virtual method invocations with static method invocations." Read more about `AnyVal` and user-defined value classes here: http://www.scala-lang.org/api/2.11.4/index.html#scala.AnyVal – Ben Reich Feb 25 '15 at 16:22
2

Previous answers are all good, I'm just throwing-in another option. This one would also work with Lists which don't have subsetOf method:

Set(1,2,3) forall(Set(3, 2, 1) contains)
vasigorc
  • 882
  • 11
  • 22