I have a Scala value of type Option[Set[String]] that I am trying to use in a collection filter method:
val opt: Option[Set[String]] = ...
collection.filter {
value =>
opt match {
case Some(set) => set.contains(value)
case None => true
}
}
If the opt value is Some(...) I want to use the enclosed Set to filter the collection, otherwise I want to include all items in the collection.
Is there a better (more idiomatic) way to use the Option (map, filter, getOrElse, etc.)?
The opt comes from an optional command line argument containing a list of terms to include. If the command line argument is missing, then include all terms.
I'd use the fact that Set[A] extends A => Boolean and pass the constant function returning true to getOrElse:
val p: String => Boolean = opt.getOrElse(_ => true)
Or:
val p = opt.getOrElse(Function.const(true) _)
Now you can just write collection.filter(p). If you want a one-liner, either of the following will work as well:
collection filter opt.getOrElse(_ => true)
collection filter opt.getOrElse(Function.const(true) _)
It seems a bit wasteful to filter on _ => true so I would prefer
opt match {
case Some(s) => collection filter s
case None => collection
}
I guess this equates to:
opt map (collection filter) getOrElse collection
Related
If I wanted to pattern match on a basic option type in Scala, I would run something along the lines of
val opt = Option(5)
val lessThanTen = opt match {
case Some(e) => if (e < 10) true else false
case None => None
}
But suppose that opt comes as a result of one of Slick's Queries, and therefore has the Lifted Embedding Type of Rep[Option[Int]]
How can I carry out the same pattern matching in a way that allows us the to see inside of the the lifted type? I.e. something along the lines of
val opt = Rep(Option(5))
val lessThanTen = opt match {
case Rep[Some(e)] => Rep[if (e < 10) true else false]
case Rep[None] => Rep[None]
}
But of course, one that compiles ;)
You can use the map method to apply some operation on the content of a Rep.
val rep: Rep[Option[Int]] = ???
val boolRep = rep.map {
case Some(i) => Some(i < 10)
case None => None
}
Even better: Option, like many other collection types in Scala, also has a similar map method, so you can write
val boolRep = rep.map(_.map(_ < 10))
In that expression, the first _ is the Option[Int], and the second one is the Int itself. In cases where the Option[Int] is None, the map method has nothing to apply the given function to, so it returns None by definition.
I want to compute something if exactly one of two options is non-empty. Obviously this could be done by a pattern match, but is there some better way?
(o1, o2) match {
case (Some(o), None) => Some(compute(o))
case (None, Some(o)) => Some(compute(o))
case _ => None
}
You could do something like this:
if (o1.isEmpty ^ o2.isEmpty)
List(o1,o2).flatMap(_.map(x=>Some(compute(x)))).head
else
None
But pattern matching is probably the better way to go.
Thanks to helpful comments from #Suma, I came up with another solutions in addition to the current ones:
Since the inputs are always in the form of Option(x):
Iterator(Seq(o1,o2).filter(_!=None))
.takeWhile(_.length==1)
.map( x => compute(x.head.get))
.toSeq.headOption
Using iterator also allows for a sequence of values to be passed to the input. The final mapping will be done if and only if one value in the sequence is defined.
Inspired by now deleted answer of pedrofurla, which was attempting to use o1 orElse o2 map { compute }, one possibility is to define xorElse, the rest is easy with it:
implicit class XorElse[T](o1: Option[T]) {
def xorElse[A >: T](o2: Option[A]): Option[A] = {
if (o1.isDefined != o2.isDefined) o1 orElse o2
else None
}
}
(o1 xorElse o2).map(compute)
Another possibility I have found is using a pattern match, but using Seq concatenation so that both cases are handled with the same code. The advantage of this approach is it can be extended to any number of options, it will always evaluate when there is exactly one:
o1.toSeq ++ o2 match {
case Seq(one) => Some(compute(one))
case _ => None
}
Just initialize a sequence and then flatten
Seq(o1, o2).flatten match {
case Seq(o) => Some(compute(o))
case _ => None
}
I have two vals, a condition and an option. Note that condition is a simple boolean, not depending on the option's value.
If condition holds true, I would like to map over the option to convert it to a result value. In all other cases, I would like to return a defaultResult.
This works and is quite readable, but I dislike the duplication of defaultResult:
val result = if (condition) {
option.map(valueToResult).getOrElse(defaultResult)
} else {
defaultResult
}
My second approach does not have duplications, but I dislike the fact that filter is abused for something that is not actually dependent on the option's value:
val result = option.filter(_ => condition).map(valueToResult).getOrElse(defaultResult)
What's a more idiomatic or otherwise better approach in Scala?
You can use Option.collect:
Returns a scala.Some containing the result of applying pf to this
scala.Option's contained value, if this option is nonempty and pf is
defined for that value.
val result = option.collect {
case x if condition => valueToResult(x)
}.getOrElse(defaultResult)
val result = option match {
case Some(value) if condition => valueToResult(value)
case _ => defaultResult
}
val result = (for (v<-option if condition) yield valueToResult(v)).getOrElse(defaultResult)
option.foldLeft(defaultResult)((d,x) => if (condition) valueToResult(x) else d)
val result = (condition match {
case true => option.map(valueToResult)
case false => None
}).getOrElse(defaultResult)
I have the following input string:
"0.3215,Some(0.5123)"
I would like to retrieve the tuple (0.3215,Some(0.5123)) with: (BigDecimal,Option[BigDecimal]).
Here is one of the thing I tried so far:
"\\d+\\.\\d+,Some\\(\\d+\\.\\d+".r findFirstIn iData match {
case None => Map[BigDecimal, Option[BigDecimal]]()
case Some(s) => {
val oO = s.split(",Some\\(")
BigDecimal.valueOf(oO(0).toDouble) -> Option[BigDecimal](BigDecimal.valueOf(lSTmp2(1).toDouble))
}
}
Using a Map and transforming it into a tuple.
When I try directly the tuple I get an Equals or an Object.
Must miss something here...
Your code has several issues, but the big one seems to be that the case None side of the match returns a Map but the Some(s) side returns a Tuple2. Map and Tuple2 unify to their lowest-common-supertype, Equals, which is what you're seeing.
I think this is what you're trying to achieve?
val Pattern = "(\\d+\\.\\d+),Some\\((\\d+\\.\\d+)\\)".r
val s = "0.3215,Some(0.5123)"
s match {
case Pattern(a,b) => Map(BigDecimal(a) -> Some(BigDecimal(b)))
case _ => Map[BigDecimal, Option[BigDecimal]]()
}
// Map[BigDecimal,Option[BigDecimal]] = Map(0.3215 -> Some(0.5123))
What is the more idiomatic way to handle an Option, map / getOrElse, or match?
val x = option map {
value => Math.cos(value) + Math.sin(value)
} getOrElse {
.5
}
or
val x = option match {
case Some(value) => Math.cos(value) + Math.sin(value)
case None => .5
}
You could always just look at the Scaladoc for Option:
The most idiomatic way to use an scala.Option instance is to treat it as a collection or monad and use map,flatMap, filter, or foreach:
val name: Option[String] = request getParameter "name"
val upper = name map { _.trim } filter { _.length != 0 } map { _.toUpperCase }
println(upper getOrElse "")
And a bit later:
A less-idiomatic way to use scala.Option values is via pattern matching:
val nameMaybe = request getParameter "name"
nameMaybe match {
case Some(name) =>
println(name.trim.toUppercase)
case None =>
println("No name value")
}
Use fold for this kind of map-or-else-default thing:
val x = option.fold(0.5){ value => Math.cos(value) + Math.sin(value) }
Obviously both are valid and I don't think one is more idiomatic than the other. That being said, using map uses the fact the Option is a Monad. This can be particularly advantageous when combining two Options. Say you have two Option[Int] that you would like to add. In this case instead of doing multiple matches it is much cleaner to use map/flatMap and it's equivalent "for comprehensions". So for your example both are valid... but for other examples using map/flatMap is often much more succinct.
Some(6).flatMap(intValue => Some(5).map(intValue + _))
or
for {
i <- Some(6)
j <- Some(5)
} yield i + j
All of them have different semantics, so in your case none of them.
map applies some function to the value inside Option, if it exists (Some, not None). Basically this is how you safely work with Options, appling function on some null value is dangeroues, cause it can throw NPE, but in case with Option it just returns None.
getOrElse simply returns either it's value or default one (which you provide as an argument). It won't do anything with the value inside the Option, you can just extract it, if you have Some, or return a default one, in case of None.
and match approach i'd say is a combination of two, cause you can apply some computation on the values and extract it from the Option