1

Want to merge val A = Option(Seq(1,2)) and val B = Option(Seq(3,4)) to yield a new option sequence

val C = Option(Seq(1,2,3,4))

This

val C = Option(A.getOrElse(Nil) ++ B.getOrElse(Nil)),

seems faster and more idiomatic than

val C = Option(A.toList.flatten ++ B.toList.flatten)

But is there a better way? And am I right that getOrElse is faster and lighter than toList.flatten?

Nat G
  • 191
  • 1
  • 15
  • When you say "faster", do you mean "has someone else performed benchmarks on this code"? Unlikely. However, the first example does not require a type conversion (`toList`). – Bob Dalgleish Sep 26 '16 at 20:05
  • I'd prefer your first approach to any of the current answers. – Alexey Romanov Sep 27 '16 at 06:26
  • @BobDalgleish yes, my feeling was that the first option has fewer conversions and thus preferable. – Nat G Sep 28 '16 at 15:27

3 Answers3

3

What about a neat for comprehension:

val Empty = Some(Nil)

val C = for {
  a <- A orElse Empty
  b <- B orElse Empty
} yield a ++ b

Creates less intermediate options.

Or, you could just do a somewhat cumbersome pattern matching:

(A, B) match {
  case (None, None) => Nil
  case (None, sb@Some(b)) => sb
  case (sa@Some(a), None) => sa
  case (Some(a), Some(b)) => Some(a ++ b)
}

I think this at least creates less intermediate collections than the double flatten.

Sascha Kolberg
  • 7,092
  • 1
  • 31
  • 37
2

Your first case:

// In this case getOrElse is not needed as the option is clearly not `None`.
// So, you can replace the following: 
val C = Option(A.getOrElse(Nil) ++ B.getOrElse(Nil))

// By this:
val C = Option(A.get ++ B.get) // A simple concatenation of two sequences.
C: Option[Seq[Int]] = Some(List(1, 2, 3, 4))

Your second case/option is wrong for multiple reasons.

val C = Option(A.toList.flatten ++ B.toList.flatten)

    Option[List[Int]] = Some(List(1, 2, 3, 4)) 
  1. It returns the incorrect type Option[List[Int]] instead of Option[Seq[Int]]

  2. It needlessly invokes toList on A & B. You could simply add the options and invoke flatten on them.

  3. It is not DRY and redundantly calls flatten on both A.toList & B.toList whereas it could call flatten on (A ++ B)

Instead of this, you could do this more efficiently:

val E = Option((A ++ B).flatten.toSeq) 
E: Option[Seq[Int]] = Some(List(1, 2, 3, 4))
Samar
  • 2,091
  • 1
  • 15
  • 18
  • In my situation the Option could be None, so I don't think `.get` will suffice. Thanks for clarifying the problems with the second case. – Nat G Sep 28 '16 at 15:27
1

Using foldLeft

Seq(Some(List(1, 2)), None).foldLeft(List.empty[Int])(_ ++ _.getOrElse(List.empty[Int]))
result: List[Int] = List(1, 2)

Using flatten twice

Seq(Some(Seq(1, 2, 3)), Some(4, 5, 6), None).flatten.flatten

result: Seq(1, 2, 3, 4, 5, 6)

Scala REPL

scala> val a = Some(Seq(1, 2, 3))
a: Some[Seq[Int]] = Some(List(1, 2, 3))

scala> val b = Some(Seq(4, 5, 6))
b: Some[Seq[Int]] = Some(List(4, 5, 6))

scala> val c = None
c: None.type = None

scala> val d = Seq(a, b, c).flatten.flatten
d: Seq[Int] = List(1, 2, 3, 4, 5, 6)
Nagarjuna Pamu
  • 14,737
  • 3
  • 22
  • 40