Evgeny Veretennikov Evgeny Veretennikov - 24 days ago 8
Scala Question

Scala: list to set using flatMap

I have class with field of type

Set[String]
. Also, I have list of objects of this class. I'd like to collect all strings from all sets of these objects into one set. Here is how I can do it already:

case class MyClass(field: Set[String])

val list = List(
MyClass(Set("123")),
MyClass(Set("456", "798")),
MyClass(Set("123", "798"))
)

list.flatMap(_.field).toSet // Set(123, 456, 798)


It works, but I think, I can achieve the same using only
flatMap
, without
toSet
invocation. I tried this, but it had given compilation error:

// error: Cannot construct a collection of type Set[String]
// with elements of type String based on a collection of type List[MyClass].
list.flatMap[String, Set[String]](_.field)


If I change type of
list
to
Set
(i.e.,
val list = Set(...)
), then such
flatMap
invocation works.

So, can I use somehow
Set.canBuildFrom
or any other
CanBuildFrom
object to invoke
flatMap
on
List
object, so that I'll get
Set
as a result?

Answer Source

The CanBuildFrom instance you want is called breakOut and has to be provided as a second parameter:

import scala.collection.breakOut

case class MyClass(field: Set[String])

val list = List(
  MyClass(Set("123")),
  MyClass(Set("456", "798")),
  MyClass(Set("123", "798"))
)

val s: Set[String] = list.flatMap(_.field)(breakOut)

Note that explicit type annotation on variable s is mandatory - that's how the type is chosen.

Edit:

If you're using Scalaz or cats, you can use foldMap as well:

import scalaz._, Scalaz._
list.foldMap(_.field)

This does essentially what mdms answer proposes, except the Set.empty and ++ parts are already baked in.