2

I have: ox: Option[A] and oxs: Option[List[A]].

I would like to:

  • Return ox.get :: oxs.get if both existed.

  • Return List(ox.get) if ox existed and oxs did not.

  • Return oxs.get if oxs existed and ox did not.

  • Return List() if both are None.

I can achieve this with ifs and matches. I was just wondering if there was any elegant idiomatic way of doing it?

EDIT: I have tested: List(ox.map(List(_)), oxs).flatten.flatten and it seems to work for all four cases, but it still looks a bit hard to understand.

Eduardo
  • 8,362
  • 6
  • 38
  • 72
  • Did you try `flatten` ? prolly this may help.. http://stackoverflow.com/questions/4730842/how-to-transform-scala-collection-of-optionx-to-collection-of-x/4730888#4730888 – Shrey May 26 '13 at 08:28
  • @Shrey: I tried `List(ox.map(List(_)), oxs).flatten.flatten`. Although the type I get as a result matches, it seems too crazy to work. – Eduardo May 26 '13 at 08:32

3 Answers3

7

Try,

ox.toList ++ oxs.toList.flatten

And if you don't mind the result being an Iterable[Int] rather than a List[Int] you can drop the initial toList,

ox ++ oxs.toList.flatten
Miles Sabin
  • 23,015
  • 6
  • 61
  • 95
  • Is there any particular reason to mention `toList` before `flatten` explicitly? – senia May 26 '13 at 17:16
  • @senia `flatten` is available on `List[List[T]]` but not on `Option[List[T]]`. – Miles Sabin May 26 '13 at 22:05
  • @MilesSabin there is an implicit `Option.option2Iterable`. – senia May 27 '13 at 03:42
  • @senia There is, but it's not applicable because there is a `flatten` method defined directly on `Option`. This is turn is not applicable because it require evidence that the content of the `Option` is itself an `Option`. Don't believe me? Give it a try: `scala> Option(List(1, 2, 3)).flatten` yields `error: Cannot prove that List[Int] <:< Option[B]`. – Miles Sabin May 27 '13 at 09:08
  • 1
    @MilesSabin: thank you. I've tested only on `2.9.3` and there is no `flatten` method in `Option` in `2.9.3`. – senia May 27 '13 at 09:18
  • 1
    @senia Well remembered ... yes, on 2.9.x the second `toList` can be dropped. – Miles Sabin May 27 '13 at 09:22
4
val oa: Option[A] = ???
val oas: Option[List[A]] = ???

val result = oa ++: oas.getOrElse( Nil ) // O(1).
val result2 = oa ++: oas.flatten // 2.9.x only. same result, less performance (O(N)).
val result3 = oa ++: oas.toList.flatten // 2.10.x. O(N).

Flatten creates new List in result2 while result reuses initial list:

val oa = Some(1)
val as = List(2,3)
val oas = Option(as)

val result = oa ++: oas.getOrElse( Nil )
// List(1, 2, 3)

val result2 = oa ++: oas.flatten
// List(1, 2, 3)

result.tail eq as // true - same object

result2.tail eq as // false - new object
senia
  • 37,745
  • 4
  • 88
  • 129
  • Although result2 may have less performance, I like it from a readability point of view (my lists will be short and this operation will be outside a critical loop) – Eduardo May 26 '13 at 08:52
  • Thanks! It is really amazing how powerful and expressive FP can be. – Eduardo May 26 '13 at 08:59
2

You can also use a for comprehension:

for (x <- ox; xs <- oxs) yield x :: xs

which is just a nicer way of writing

ox.flatMap(x => oxs.map(xs => x :: xs))
ethzero
  • 281
  • 1
  • 6