Reputation: 6822
In Scala, to express this:
val opt:Option[String] = Some("a")
if(opt.isEmpty) "" else opt.get
you can use this fold
pattern on Options
:
opt.fold("")(_)
However fold
on collections behaves differently. How can I achieve the same for collections, without having to write:
case class Container(description: String, collection: Seq)
val col = Seq()
if(col.nonEmpty) {
Some(Container("some description", col))
} else {
None
}
In other words, how can I elegantly return Some()
collection only when it is not empty?
Upvotes: 1
Views: 1187
Reputation: 40500
Note, that your examples for Option
and Seq
aren't equivalent: in the former case, the result is the value that was inside the Option
or an empty string, while in the latter case, you end up returning the collection itself, not its contents.
You can do an equivalent of what you did with Option
with a fold
on collections too, depending on how you want to deal with the multiple values in the collection. For example:
seqOfStrings.foldLeft("") { _ + _ }
would concatenate all of the strings in the collection into one long string (you could get the same result with seqOfStrings.mkString
). Note, that this uses foldLeft
rather than fold
, because the former is (supposed to be) parallelizable, and is not guaranteed to process the results in order. Here is an example with fold
:
seqOfInts.fold(0) { _ + _ }
This is, of course, the same as seqOfInts.sum
The idea of fold
(left/right) is combining ("folding") all the elements of a collection into a single value. Option
is a special case, because it can only ever contain a single element, so the transformer function does not need to take two arguments, and looks a lot like the (opposite of) getOrElse
- thus your confusion.
What you are trying to do with the collection is not really a use case for fold. You could use headOption
for that as other answers suggested, or, better yet, pattern match:
col match {
case Seq() => None
case x => Some(Container("some description", x))
}
You could wrap it into the companion object, to look prettier:
object Container {
def apply[T](seq: Seq[T]) = seq match {
case Seq() => None
case s => Container("some description", x))
}
}
Now, you can just do Container(seq)
elsewhere.
Upvotes: 2
Reputation: 1226
Your expectation from fold
is wrong, it is not about Option
s it is about working with collection and it works with Option
because they are also collections.
With fold
you are providing a start value with first parameter and providing a function which will be applied to collection.
About your question:
Code you shared looks fine, but if you really want to map some option you can use headOption
and map it to value you want but this won't make your code more elegant or clear
Upvotes: 1
Reputation: 1644
This is a bit shorter, although also not really elegant:
Option(col.nonEmpty).collect { case true => Container("some description", col) }
Or, but IMO even uglier (I just saw that Hüseyin suggested it as well):
col.headOption.map(Container("some description", col))
Upvotes: 0