将 Scala Iterable[T] 转换为 Option[Iterable[S]],如果所有 T 都是 S 的实例,则填充?

Posted

技术标签:

【中文标题】将 Scala Iterable[T] 转换为 Option[Iterable[S]],如果所有 T 都是 S 的实例,则填充?【英文标题】:Converting a Scala Iterable[T] to an Option[Iterable[S]], populated if ALL the T are instances of S? 【发布时间】:2021-09-09 01:10:53 【问题描述】:

我有一种模糊的感觉,这种方法可能存在(可能在Cats?) - 目的是转为Iterable[T]Set[T]是我真正感兴趣的) 转换为Option[Iterable[S]] - 结果是:

Some[Iterable[S]] :如果Iterable[T] 中的所有 条目是S 的实例 None :如果Iterable[T] 中的任何条目不是S 的实例

我知道在 Cats 中已经有一种类似的方法,由 Alternative 授予 - 它是 separate

import cats.implicits._
import alleycats.std.set._

val stringsAndInts: Set[Either[String, Int]] = Set(Right(6),Left("Foo"))
val (strings: Set[String], ints: Set[Int]) = stringsAndInts.separate

...这对Set[Either[A,B] 很有效——但对于这个问题,我只对任何旧的Set[T] 感兴趣。

collect 呢?

此代码使用 Scala 标准库方法 collect 将编译 - 但请注意,它始终会生成 Set[S] - 即使某些 setOfT 不是 S 的实例:

val setOfT: Set[T] = ???
val setOfS: Set[S] = setOfT.collect case s: S => s

【问题讨论】:

如果你有任意类型,由于类型擦除,这不能开箱即用。您要么需要为某些类型的子集编写特定的方法,要么创建自己的 typeclass 来验证数据。 - 或者您可以使用运行时反射并为类型擦除打开错误之门。 【参考方案1】:

考虑无形类型安全铸造

import shapeless._
import syntax.typeable._

val xs: Set[Any] = Set("picard", "worf")

xs.cast[Set[Int]]
// res1: Option[Set[Int]] = None

xs.cast[Set[String]]
// res2: Option[Set[String]] = Some(value = Set("picard", "worf"))

如果您希望拥有与 collect 类似的功能并且在类路径中已经有了猫,那么这里是单线

import alleycats.std.set._
animals.map(implicitly[ClassTag[Dog]].unapply).sequence

Option[Set[Dog]] 的类型。

【讨论】:

你好马里奥!很棒的东西——所以这是使用 Scala 宏来绕过类型擦除,对吗? github.com/milessabin/shapeless/blob/main/core/src/main/scala/… @RobertoTyley 是的,与ClassTag#unapply 不同,它也适用于参数化类型。 docs【参考方案2】:

正如 Luis Miguel Mejía Suárez 在评论中指出的那样,由于类型擦除,我想要的方法无法用于任意类型。

最后,我写了这样的代码:

val animals: Set[Animal] = ???
val optDogs: Option[Set[Dog]] = animals.foldLeft(Option(Set.empty[Dog])) 
  case (oDogs, dog: Dog) => oDogs.map(_ + dog)
  case _ => None

...很高兴看到更好的实现!

【讨论】:

你可以只做aminals.collect case dog: Dog => dog 并检查它是否为非空。你也可以做animals.traverse case dog: Dog => Some(dog); case _ => None 或者你可以使用你自己的 Typeclass 来概括它 trait Is[T, U] def check(t: T): Option[U] 最终会做相同的match 并且可以与相同的traverse 一起使用,但它似乎 *Shapeless 已经为你解决了 :)

以上是关于将 Scala Iterable[T] 转换为 Option[Iterable[S]],如果所有 T 都是 S 的实例,则填充?的主要内容,如果未能解决你的问题,请参考以下文章

在 Scala 中,从初始对象和生成下一个对象的函数创建 O(1) 内存 Iterable

如何将 Iterable[String] 分组为(键,出现次数)元组

如何将 Int 转换为有序 (Scala)

Scala将Collection转换为按键映射的最佳方法是什么?

scala集合和Java集合对应转换操作

WrappedArray$ofRef 不能转换为 scala.collection.immutable.Seq