Scala:使用flatMap设置的列表

时间:2017-09-25 12:34:20

标签: scala collections

我的课程类型为Set[String]。另外,我有这个类的对象列表。我想将所有这些对象集中的所有字符串收集到一个集合中。我已经可以这样做了:

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)

它有效,但我认为,我只能使用flatMap,而不需要toSet调用来实现相同的功能。我试过这个,但它给出了编译错误:

// 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)

如果我将list的类型更改为Set(即val list = Set(...)),则此类flatMap调用有效。

那么,我可以使用某种方式Set.canBuildFrom或任何其他CanBuildFrom对象来调用flatMap对象上的List,这样我就会得到Set结果?

3 个答案:

答案 0 :(得分:5)

您想要的CanBuildFrom实例名为breakOut,必须作为第二个参数提供:

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)

请注意,变量s上的显式类型注释是必需的 - 这就是选择类型的方式。

修改

如果您使用的是Scalazcats,则还可以使用foldMap

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

除了mdmSet.empty部分已经出现之外,这基本上与++的答案提出的内容基本相同。

答案 1 :(得分:4)

flatMap在Scala中的工作方式是它只能为相同类型的包装器移除一个包装器,即List[List[String]] -> flatMap -> List[String]

如果将flatMap应用于不同的包装器数据类型,那么您将始终获得最终结果作为更高级别的包装器数据类型,即List[Set[String]] -> flatMap -> List[String]

如果你想在不同的包装类型上应用flatMap,即List[Set[String]] -> flatMap -> Set[String],你有两个选择: -

  1. 将一个数据类型包装器显式强制转换为另一个,即list.flatMap(_.field).toSet

  2. 通过提供隐式转换器即。 implicit def listToSet(list: List[String]): Set[String] = list.toSet您可以获得val set:Set[String] = list.flatMap(_.field)

  3. 只有这样你才能实现的目标。

    结论: - 如果你在2个包装数据类型上应用flatMap,那么你总是得到最终结果作为op类型,它位于包装数据类型之上,即List[Set[String]] -> flatMap -> List[String],如果你想转换或转换为不同的数据类型,然后你需要隐式或显式地转换它。

答案 2 :(得分:1)

您可以提供具体的CanBuildFrom,但为什么不使用折叠?

list.foldLeft(Set.empty[String]){case (set, myClass) => set ++ myClass.field}

仍然只是一次通过该集合,如果您确定list不为空,您甚至可以改为使用reduceLeft