How to implicitly wrap a value that can be null or an array into an Scala Option

萝らか妹 提交于 2019-12-06 01:55:11

I don't think your version with getOrElse is that bad (you can make it a little shorter by removing the [Array[String]] after Option, since that can be inferred). If you want something even more concise, the following works:

for (str <- Option(myInstance.getSomeStrings).flatten) ...

You could also use the fact that Option has a foreach:

for {
  strings <- Option(myInstance.getSomeStrings)
  str <- strings
} ...

Note that you can't use yield here, for the reason that drexin highlights in a comment below.

Or you could pimp MyClass:

implicit def withNullWrapper(c: MyClass) = new {
  def getSomeStringsOrNot() = Option(c.getSomeStrings).getOrElse(Array[String]())
}

for (str <- myInstance.getSomeStringsOrNot) ...

edit:

A mapas well as a flatMap always have to return the same type, on which they are called. If you have a List, you will always get a List back from map. The same is true for an Option. If you try to mix 2 types in a flatMap, this will most likely not work. What should

Some(Array(1,2)).flatMap { x => 
  x.map { _ * 2 }
}

return? Some(2,4) is not possible. So you get a type error. For this reason you have to do a nested map { map } instead of flatMap { map }.

In your case it would work like this:

case class A(b: B)
case class B(c: String)

val result = for(as <- Option(Array(A(B("foo")), A(B("bar"))))) yield {
  for(a <- as; b <- Option(a.b); c <- Option(b.c)) yield {
    c
  }
}

The first for takes an Option[Array[A]] and returns an Option[Array[String]]. The nested for takes an Array[A] and returns an Array[String]. They both satisfy the monad laws. At the end you can safely call getOrElse on result to unwrap the value if you want to.

original:

You could just do

val result = Option(myInstance.getSomeStrings).map { x =>
  x.map { y =>
    // do stuff with strings
  }
}

or

val result = for(x <- Option(myInstance.getSomeStrings)) yield {
  x.map { y =>
    // do stuff with strings
  }
}

You don't need to write the types because of the type inference and you don't need getOrElse, because the map will not be executed for None. You can then simply do a getOrElse on result, if you need to unwrap the value.

Simple rule: where there's a null, make it an Option. So:

for {
  array <- Option(myInstance.getSomeStrings)
  element <- array
  thingy <- Option(element.method)
} yield thingy

Only that won't work. Because of the array, it will return multiple elements, but because the first generator is an Option, it will return an Option. These two elements are adverse: you can't return an Option of multiple elements.

The simplest alternative to fix the problem is to convert the Option into an iterator or a collection (according to your taste). Like this:

for {
  array <- Option(myInstance.getSomeStrings).toSeq
  element <- array
  thingy <- Option(element.method)
} yield thingy

Note that the second Option need not be touched: the one that caused the problem was the one as the first generator. Option anywhere other than the first generator is not a problem.

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