Need to convert a Seq[Option[A]] to Option[Seq[A]]

谁都会走 提交于 2020-01-06 05:26:33

问题


USE CASE

I have a list of files that can might have a valid mime type or not. In my code, I represent this using an Option.

I need to convert a Seq[Option[T]] to Option[Seq[T]] so that I do not process the list if some of the files are invalid.

ERROR

This is the error in the implementation below:

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

IMPLEMENTATION

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])
  }

This implementation returns Option[Any] instead of Option[Seq[A] as the type of the liftOptionItem[A] does not fit in.


回答1:


If you use TypeLevel Cats:

import cats.implicits._

List(Option(1), Option(2), Option(3)).traverse(identity)

Returns:

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

You have to use List so use a toList first:

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



回答2:


using 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



回答3:


Tail-recursive Solution: It returns None if any one of the seq element is 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)
}



回答4:


Dealing with None in case statements is the reason for returning the Option[Seq[Any]] type in stead of Option[Seq[A]] type. We need to make the function liftOptionItem[A] to return Option[Seq[Any]] type. And the compilation error can be fixed with the following changes in both the functions.(Because fold does not go in any particular order, there are constraints on the start value and thus return value , the foldLeft is used in stead of 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])
 }

Now, code compiles.

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



回答5:


If you dont want to use functional libraries like cats or Scalaz you could use a 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
      }


来源:https://stackoverflow.com/questions/52546860/need-to-convert-a-seqoptiona-to-optionseqa

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!