Необходимо преобразовать Seq [Option [A]] в Option [Seq [A]] - PullRequest
0 голосов
/ 28 сентября 2018

USE CASE

У меня есть список файлов, которые могут иметь допустимый тип MIME или нет.В моем коде я представляю это с помощью Option.

Мне нужно преобразовать 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] не подходит.

Ответы [ 5 ]

0 голосов
/ 06 июня 2019

Если вы не хотите использовать функциональные библиотеки, такие как cats или 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
      }
0 голосов
/ 01 октября 2018

Работа с None в случае, если выписка является причиной для возврата Option[Seq[Any]] type вместо Option[Seq[A]] type.Нам нужно заставить функцию liftOptionItem[A] вернуть Option[Seq[Any]] type.И ошибка компиляции может быть исправлена ​​следующими изменениями в обеих функциях. (Поскольку fold не идет в каком-либо конкретном порядке, существуют ограничения на start value и, таким образом, return value, foldLeft используется ввместо 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))
0 голосов
/ 28 сентября 2018

Хвостово-рекурсивное решение: возвращается None, если любой из элементов seq равен 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)
}
0 голосов
/ 28 сентября 2018

с использованием скаляза:

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
0 голосов
/ 28 сентября 2018

Если вы используете 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)
...