【问题标题】:Need to convert a Seq[Option[A]] to Option[Seq[A]]需要将 Seq[Option[A]] 转换为 Option[Seq[A]]
【发布时间】:2019-03-03 22:49:54
【问题描述】:

用例

我有一个文件列表,这些文件可能具有或不具有有效的 mime 类型。 在我的代码中,我使用选项来表示这一点。

我需要将 Seq[Option[T]] 转换为 Option[Seq[T]] 以便在某些文件无效时不处理列表。

错误

这是以下实现中的错误:

found   : (Option[Seq[A]], Option[A]) => Option[Seq[A]]
[error]  required: (Option[Any], Option[Any]) => Option[Any]
[error]     s.fold(init)(liftOptionItem[A])

实施

def liftOptionItem[A](acc: Option[Seq[A]], itemOption: Option[A]): Option[Seq[A]] = {
    {
      acc match {
        case None => None
        case Some(items) =>
          itemOption match {
            case None => None
            case Some(item) => Some(items ++ Seq(item))
          }
      }
    }
  }

  def liftOption[A](s: Seq[Option[A]]): Option[Seq[A]] = {
    s.fold(Some(Seq()))(liftOptionItem[A])
  }

此实现返回Option[Any] 而不是Option[Seq[A],因为liftOptionItem[A] 的类型不适合。

【问题讨论】:

标签: scala fold scala-option


【解决方案1】:

如果您不想使用 cat 或 Scalaz 等函数库,可以使用 foldLeft

def seqToOpt[A](seq: Seq[Option[A]]): Option[Seq[A]] =
    seq.foldLeft(Option(Seq.empty[A])){
        (res, opt) =>
          for {
            seq <- res
            v <- opt
          } yield seq :+ v
      }

【讨论】:

    【解决方案2】:

    处理None in case 语句是返回Option[Seq[Any]] type 而不是Option[Seq[A]] type 的原因。我们需要使函数 liftOptionItem[A] 返回Option[Seq[Any]] type。并且编译错误可以通过以下两个函数的更改来修复。(因为 fold 没有按任何特定顺序排列,所以 start value 有限制,因此 return valuefoldLeft 用于而不是fold。)

     def liftOptionItem[A](acc: Option[Seq[Any]], itemOption: Option[A]): Option[Seq[Any]] = {
         {
           acc match {
             case None => Some(Nil)
             case Some(items)=>
               itemOption match {
                 case None => Some(items ++ Seq("None"))
                 case Some(item) => Some(items ++ Seq(item))
               }
           }
         }
       }
    
     def liftOption[A](s: Seq[Option[A]]): Option[Seq[Any]] = {
       s.foldLeft(Option(Seq[Any]()))(liftOptionItem[A])
     }
    

    现在,代码编译。

    在 Scala REPL 中:

    scala> val list1 = Seq(None,Some(21),None,Some(0),Some(43),None)
    list1: Seq[Option[Int]] = List(None, Some(21), None, Some(0), Some(43), None)
    
    scala> liftOption(list1)
    res2: Option[Seq[Any]] = Some(List(None, 21, None, 0, 43, None))
    
    scala> val list2 = Seq(None,Some("String1"),None,Some("String2"),Some("String3"),None)
    list2: Seq[Option[String]] = List(None, Some(String1), None, Some(String2), Some(String3), None)
    
    scala> liftOption(list2)
    res3: Option[Seq[Any]] = Some(List(None, String1, None, String2, String3, None))
    

    【讨论】:

      【解决方案3】:

      使用 scalaz:

      import scalaz._
      import Sclaza._
      
      val x:List[Option[Int]] = List(Option(1))
      x.sequence[Option, Int] //returns Some(List(1))
      
      val y:List[Option[Int]] = List(None, Option(1))
      y.sequence[Option, Int] // returns None
      

      【讨论】:

        【解决方案4】:

        尾递归解决方案:如果seq元素中的任何一个为None,则返回None。

        def seqToOption[T](s: Seq[Option[T]]): Option[Seq[T]] = {
          @tailrec
          def seqToOptionHelper(s: Seq[Option[T]], accum: Seq[T] = Seq[T]()): Option[Seq[T]] = {
            s match {
              case Some(head) :: Nil => Option(head +: accum)
              case Some(head) :: tail => seqToOptionHelper(tail, head +: accum)
              case _ => None
            }
          }
          seqToOptionHelper(s)
        }
        

        【讨论】:

          【解决方案5】:

          如果你使用 TypeLevel Cats:

          import cats.implicits._
          
          List(Option(1), Option(2), Option(3)).traverse(identity)
          

          返回:

          Option[List[Int]] = Some(List(1, 2, 3))
          

          你必须使用List,所以首先使用toList

          Seq(Option(1), Option(2), Option(3)).toList.traverse(identity).map(_.toSeq)
          

          【讨论】:

          • 从哪里导入或如何声明identity
          • @NefedovEfim, identity 是自动从 Predef 自动导入到 Scala 中的。所以你只需要import cats.implicits._。如果有的话,您可能需要限定遍历,Seq(Option(1), Option(2), Option(3)).toList.traverse[Option, Int](identity).map(_.toSeq) 但这就是您所需要的,当然,将猫安装到您的项目中。 ;)
          猜你喜欢
          • 2021-08-31
          • 2020-07-16
          • 1970-01-01
          • 1970-01-01
          • 2014-08-25
          • 2012-10-11
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多