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”关键字,因为您没有进行任何重要的模式匹配