1

I'm wondering what the idiomatic way in Scala would be to convert a Seq of Option[A] to an Option[Seq[A]], where the result is None if any of the input options were None.

John
  • 2,575
  • 1
  • 17
  • 29
  • 3
    See https://stackoverflow.com/questions/28753777/converting-listoptiona-to-an-optionlista-in-scala and https://stackoverflow.com/questions/52546860/need-to-convert-a-seqoptiona-to-optionseqa. – Alexey Romanov Jun 15 '21 at 18:16

2 Answers2

3

The idiomatic way is probably to use what is generally called traverse.

I'd recommend reading Cats' documentation about it: https://typelevel.org/cats/typeclasses/traverse.html

With Cats, it would be as easy as:

import cats.implicits

val list = List(Some(1), Some(2), None)
// list: List[Option[Int]] = List(Some(1), Some(2), None)

val traversed = list.traverse(identity)
// traversed: Option[List[Int]] = None

// Or in this specific case:
val sequenced = list.sequence
// sequenced: Option[List[Int]] = None
Gaël J
  • 11,274
  • 4
  • 17
  • 32
3

Standard library provides partitionMap which helps implement sequence operation

_.partitionMap(_.toRight("")) match {
  case (Nil, xs) => Some(xs)
  case _ => None
}

Here is a possible generic implementation as extension method

extension [CC[x] <: IterableOps[x, CC, CC[x]], A](xs: CC[Option[A]])
  def sequence: Option[CC[A]] =
    xs.partitionMap(_.toRight("")) match {
        case (lefts, as) if lefts.isEmpty => Some(as)
        case _ => None
    }
Mario Galic
  • 47,285
  • 6
  • 56
  • 98
  • 2
    Very nice, but use of `Nil` restricts the possible collection types. Alternative: `.pipe(tup => if (tup._1.isEmpty) Some(tup._2) else None)` – jwvh Jun 15 '21 at 20:18