Scala:使用flatMap设置的列表
我的类的字段类型为Scala:使用flatMap设置的列表,scala,collections,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[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
对象调用List
对象上的flatMap
,这样我就可以得到Set
。您可以提供一个特定的canBuildFrom
,但为什么不使用折叠呢
list.foldLeft(Set.empty[String]){case (set, myClass) => set ++ myClass.field}
仍然只需通过集合一次,如果您确定
列表不为空,您甚至可以使用reduceLeft
。您可以提供特定的CanBuildFrom
,但为什么不使用折叠呢
list.foldLeft(Set.empty[String]){case (set, myClass) => set ++ myClass.field}
仍然只需通过集合一次,如果您确定列表不为空,您甚至可以使用reduceLeft
。flatMap在Scala中的工作方式是,它只能为相同类型的包装移除一个包装,即list[list[String]->flatMap->list[String]
如果在不同的包装器数据类型上应用flatMap,那么最终结果将始终作为更高级别的包装器数据类型,即List[Set[String]->>flatMap->List[String]
如果要在不同的包装类型上应用flatMap,即中的List[Set[String]->>flatMap->Set[String]
,则有2个选项:-
显式地将一个数据类型包装转换为另一个,即list.flatMap(u.field).toSet
或
通过提供隐式转换器,即隐式def listToSet(list:list[String]):Set[String]=list.toSet
,您可以获得val Set:Set[String]=list.flatMap(u.field)
只有到那时,你想要达到的目标才会实现
结论:-如果在2个包装数据类型上应用flatMap,那么您将始终以op类型的形式获得最终结果,op类型位于包装数据类型的顶部,即List[Set[String]->flatMap->List[String]
如果要转换或强制转换为不同的数据类型,则需要隐式或显式强制转换它。flatMap在Scala中的工作方式是,对于相同类型的包装,它只能删除一个包装,即List[List[String]->flatMap->List[String]
如果在不同的包装器数据类型上应用flatMap,那么最终结果将始终作为更高级别的包装器数据类型,即List[Set[String]->>flatMap->List[String]
如果要在不同的包装类型上应用flatMap,即中的List[Set[String]->>flatMap->Set[String]
,则有2个选项:-
显式地将一个数据类型包装转换为另一个,即list.flatMap(u.field).toSet
或
通过提供隐式转换器,即隐式def listToSet(list:list[String]):Set[String]=list.toSet
,您可以获得val Set:Set[String]=list.flatMap(u.field)
只有到那时,你想要达到的目标才会实现
结论:-如果在2个包装数据类型上应用flatMap,那么您将始终以op类型的形式获得最终结果,op类型位于包装数据类型的顶部,即
List[Set[String]->flatMap->List[String]
如果要转换或强制转换为不同的数据类型,则需要隐式或显式强制转换。所需的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
上的显式类型注释是必需的-这就是选择类型的方式
编辑:
如果您使用的是Scalaz
或cats
,也可以使用foldMap
:
import scalaz._, Scalaz._
list.foldMap(_.field)
这基本上就是
mdm
的答案所建议的,除了集合。空的和++
部分已经被烘焙了。您想要的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
上的显式类型注释是必需的-这就是选择类型的方式
编辑:
如果您使用的是Scalaz
或cats
,也可以使用foldMap
:
import scalaz._, Scalaz._
list.foldMap(_.field)
这基本上就是mdm
的答案所建议的,除了集合。空的和++
部分已经被烤好了。这里不需要使用case
,因为foldLeft
接受带有两个参数的函数,即(\u++.field)
您肯定是对的@Oleg,但是为了清楚起见,我把它明确了。你仍然可以省略“case”关键字,因为你没有进行任何重要的模式匹配这里没有必要使用case
,因为foldLeft
接受带有两个参数的函数,即(++.field)
你当然是对的@Oleg,但为了清晰起见,我把它明确了。您仍然可以省略“case”关键字,因为您没有进行任何重要的模式匹配