Need to convert a Seq[Option[A]] to Option[Seq[A]] - scala

USE CASE
I have a list of files that can might have a valid mime type or not.
In my code, I represent this using an Option.
I need to convert a Seq[Option[T]] to Option[Seq[T]] so that I do not process the list if some of the files are invalid.
ERROR
This is the error in the implementation below:
found : (Option[Seq[A]], Option[A]) => Option[Seq[A]]
[error] required: (Option[Any], Option[Any]) => Option[Any]
[error] s.fold(init)(liftOptionItem[A])
IMPLEMENTATION
def liftOptionItem[A](acc: Option[Seq[A]], itemOption: Option[A]): Option[Seq[A]] = {
{
acc match {
case None => None
case Some(items) =>
itemOption match {
case None => None
case Some(item) => Some(items ++ Seq(item))
}
}
}
}
def liftOption[A](s: Seq[Option[A]]): Option[Seq[A]] = {
s.fold(Some(Seq()))(liftOptionItem[A])
}
This implementation returns Option[Any] instead of Option[Seq[A] as the type of the liftOptionItem[A] does not fit in.

If you use TypeLevel Cats:
import cats.implicits._
List(Option(1), Option(2), Option(3)).traverse(identity)
Returns:
Option[List[Int]] = Some(List(1, 2, 3))
You have to use List so use a toList first:
Seq(Option(1), Option(2), Option(3)).toList.traverse(identity).map(_.toSeq)

using scalaz:
import scalaz._
import Sclaza._
val x:List[Option[Int]] = List(Option(1))
x.sequence[Option, Int] //returns Some(List(1))
val y:List[Option[Int]] = List(None, Option(1))
y.sequence[Option, Int] // returns None

If you dont want to use functional libraries like cats or Scalaz you could use a foldLeft
def seqToOpt[A](seq: Seq[Option[A]]): Option[Seq[A]] =
seq.foldLeft(Option(Seq.empty[A])){
(res, opt) =>
for {
seq <- res
v <- opt
} yield seq :+ v
}

Tail-recursive Solution: It returns None if any one of the seq element is None.
def seqToOption[T](s: Seq[Option[T]]): Option[Seq[T]] = {
#tailrec
def seqToOptionHelper(s: Seq[Option[T]], accum: Seq[T] = Seq[T]()): Option[Seq[T]] = {
s match {
case Some(head) :: Nil => Option(head +: accum)
case Some(head) :: tail => seqToOptionHelper(tail, head +: accum)
case _ => None
}
}
seqToOptionHelper(s)
}

Dealing with None in case statements is the reason for returning the Option[Seq[Any]] type in stead of Option[Seq[A]] type. We need to make the function
liftOptionItem[A] to return Option[Seq[Any]] type. And the compilation error can be fixed with the following changes in both the functions.(Because fold does not go in any particular order, there are constraints on the start value and thus return value , the foldLeft is used in stead of fold.)
def liftOptionItem[A](acc: Option[Seq[Any]], itemOption: Option[A]): Option[Seq[Any]] = {
{
acc match {
case None => Some(Nil)
case Some(items)=>
itemOption match {
case None => Some(items ++ Seq("None"))
case Some(item) => Some(items ++ Seq(item))
}
}
}
}
def liftOption[A](s: Seq[Option[A]]): Option[Seq[Any]] = {
s.foldLeft(Option(Seq[Any]()))(liftOptionItem[A])
}
Now, code compiles.
In Scala REPL:
scala> val list1 = Seq(None,Some(21),None,Some(0),Some(43),None)
list1: Seq[Option[Int]] = List(None, Some(21), None, Some(0), Some(43), None)
scala> liftOption(list1)
res2: Option[Seq[Any]] = Some(List(None, 21, None, 0, 43, None))
scala> val list2 = Seq(None,Some("String1"),None,Some("String2"),Some("String3"),None)
list2: Seq[Option[String]] = List(None, Some(String1), None, Some(String2), Some(String3), None)
scala> liftOption(list2)
res3: Option[Seq[Any]] = Some(List(None, String1, None, String2, String3, None))

There is no really "beautiful" way to make this with out scalaz or cats.
But you can try something like this.
def seqToOpt[A](seq: Seq[Option[A]]): Option[Seq[A]] = {
val flatten = seq.flatten
if (flatten.isEmpty) None
else Some(flatten)
}

Related

Short circuit Map Operation If Try Fails

I have a function like this:
def foo(item: Item) : Option[Int] = Try{
// Some code that can blow up
}.toOption
I have a list of items and I want to map through them, and apply the above function. But if the function above blows up and returns a None then the result of the map should be an error:
items.map{
item => foo(item)
}
Is map not the right thing to do here? It doesn't seem like it
This is called traverse. If you can use cats, it is as simple as:
import cats.implicits._
val result = items.traverse(foo) // Option[List[Int]]
If not, you can implement it pretty easily:
def traverse[A, B](data: List[A])(f: A => Option[B]): Option[List[B]] = {
#annotation.tailrec
def loop(remaining: List[A], acc: List[B]): Option[List[B]] =
remaining match {
case a :: as => f(a) match {
case Some(b) => loop(remaining = as, b :: acc)
case None => None
}
case Nil => Some(acc.reverse)
}
loop(remaining = data, acc = List.empty)
}
Which you can use like:
val result = traverse(items)(foo) // Option[List[Int]]
(however, I would suggest you to use cats instead, since it is more general).
For out-of-the-box short-circuiting, consider wrapping the list-mapping with Try like so
def fooUnsafe(item: Item): Int = // might throw
Try(items.map(fooUnsafe))
If you wish to keep def foo(item: Item) : Option[Int] signature then the following will also short-circuit
Try(list.map(v => foo(v).get))

Avoid passing accumulation parameter in tail recursive call

Say I want to do a simple conversion of strings to ints thus:
List("1", "3", "55", "x", "7") => List(1, 3, 55, 7)
One way to do this would be the following recursive call:
def recurse1(strs: List[String]): List[Int] = strs match {
case h :: t =>
try {
h.toInt :: recurse1(t)
}
catch {
case _ : java.lang.NumberFormatException =>
recurse1(t)
}
case _ =>
List()
}
However this cannot be compiled as tail recursive due to line 4 in the code. So to get around this I can redefine the function as follows:
def recurse2(strs: List[String], accum: List[Int] = List()): List[Int] = strs match {
case h :: t =>
try {
recurse2(t, h.toInt :: accum)
}
catch {
case _ : java.lang.NumberFormatException =>
recurse2(t, accum)
}
case _ =>
accum.reverse
}
So my question is this. Is there an idiom I can use in scala that will allow me to do this tail recursively but without having to pass a variable to accumulate the values?
Maybe your recurse method is just for illustration but for completeness I'll add to #pamu's answer how to use standard functions:
def foo(ss: List[String]): List[Int] =
ss.map(s => Try(s.toInt).toOption)
.filter(_.isDefined)
.map(_.get)
or
def foo(ss: List[String]): List[Int] =
ss.map(s => Try(s.toInt))
.collect { case Success(n) => n }
Usually, I see programmers write a helper function which takes many arguments (internally) which is specific to method/algorithm you are using. They write a minimal interface function around the ugly internal function which is tail recursive and takes only inputs required and hides the internal mechanism.
def reverse(input: List[Sting]): List[Int] = {
def helper(strs: List[String], accum: List[Int] = List()): List[Int] =
strs match {
case h :: t =>
try {
helper(t, h.toInt :: accum)
}
catch {
case _ : java.lang.NumberFormatException =>
helper(t, accum)
}
case _ =>
accum.reverse
}
helper(input, List.empty[Sting])
}

Scala `map` but exit early on `Failure`

If I have a Seq, I can map over it.
val ss = Seq("1", "2", "3")
println(ss.map(s => s.toInt)) // List(1, 2, 3)
But sometimes, the function that you pass to map can fail.
val ss = Seq("1", "2", "c")
println(ss.map(s => try { Success(s.toInt) } catch { case e: Throwable => Failure(e) })) // List(Success(1), Success(2), Failure(java.lang.NumberFormatException: For input string: "c"))
This last one will return a Seq[Try[Int]]. What I really want though is a Try[Seq[Int]], where if any one of the mapping is a Failure, it stops the iteration and returns the Failure instead. If there is no error, I want it to just return all the converted elements, unpacked from the Try.
What is the idiomatic Scala way to do this?
You may be overthinking this. The anonymous function in your map is essentially the same as Try.apply. If you want to end up with Try[Seq[Int]] then you can wrap the Seq in Try.apply and map within:
scala> val ss = Try(Seq("1", "2", "c").map(_.toInt))
ss: scala.util.Try[Seq[Int]] = Failure(java.lang.NumberFormatException: For input string: "c")
If any of the toInts fails, it will throw an exception and stop executing, and become a Failure.
Not sure it's idiomatic, but I would do something like this:
import util.{Try, Success, Failure}
import collection.mutable.ListBuffer
def toInt(s: String) =
// Correct usage would be Try(s.toInt)
try {
Success(s.toInt)
}
catch {
case e: Throwable => Failure(e)
}
def convert[A](ss: Seq[String], f: String => Try[A]) = {
ss.foldLeft(Try(ListBuffer[A]())) {
case (a, s) =>
for {
xs <- a
x <- f(s)
}
yield xs :+ x
}.map(_.toSeq)
}
scala> convert(List("1", "2"), toInt)
scala.util.Try[Seq[Int]] = Success(List(1, 2))
scala> convert(List("1", "c"), toInt)
scala.util.Try[Seq[Int]] = Failure(java.lang.NumberFormatException: For input string: "c")
If you really want to exit early instead of skipping elements you can use good old recursion:
def convert[A](ss: Seq[String], f: String => Try[A]) = {
#annotation.tailrec
def loop(ss: Seq[String], acc: ListBuffer[A]): Try[Seq[A]] = {
ss match {
case h::t =>
f(h) match {
case Success(x) => loop(t, acc :+ x)
case Failure(e) => Failure(e)
}
case Nil =>
Success(acc.toSeq)
}
}
loop(ss, ListBuffer[A]())
}

Trying to return a Map but an Iterable is currently being returned

Why is my val pairOpt a Option[Option[String,String]]?
I am trying to have it so it returns Option[(String, String)].
def blah(..): Map[String, String] = {
val map: Map[String, String] = //
val boolTry = Try(map.getOrElse("key1", "").trim.toBoolean)
val intTry = Try(map.getOrElse("key2, "").trim.toInt)
val pairOpt: Option[Option[(String, String)]] = for {
b <- boolTry.toOption
i <- intTry.toOption
} yield {
val res: Option[String] = (b, i) match {
case (true, 1) => Some("a")
case (false, 2 | 3 | 7) => Some("b")
case (true, 5 | 9 | 11) => Some("c")
case _ => None
}
res.map("foo" -> _)
}
map ++ pairOpt // map + ("foo" -> "c")
}
The return value is also currenly Iterable[Product with Serializable] when I want it to be Map[String,String].
What am I missing here?
You get an Option[Option[..]] because you have two "layers" of "optionality" here that you have to flatten:
If one of boolTry or intTry is a Failure - you'll get None
Else, if they're both Success but their values don't match anything, you'll get Some(None)
Otherwise, you'll get Some(Some(..))
More generally, given a opt: Option[V], the type of an expression of the form:
for {
x <- opt
..
} yield {
val y: T
y
}
is Option[T] - because it translates into opt.flatMap(...).map(...) which preserves the "external" structure (be it an Option, List, Seq etc.). In your case, T = Option[(String, String)], so the result has the type Option[Option[(String, String)]]. To fix this - you can use flatten:
val pairOpt: Option[(String, String)] = (for {
b <- boolTry.toOption
i <- intTry.toOption
} yield {
// same as you did...
}).flatten
Which would also fix the issue with the method's return type (now map ++ pairOpt will have the type Map[String, String] as expected).
To avoid this call to flatten - perhaps a cleaner way to achieve the same would be:
val maybeTuple: Option[(Boolean, Int)] = boolTry.flatMap(b => intTry.map((b, _))).toOption
val pairOpt: Option[(String, String)] = maybeTuple.flatMap {
case (true, 1) => Some("a")
case (false, 2 | 3 | 7) => Some("b")
case (true, 5 | 9 | 11) => Some("c")
case _ => None
}.map("foo" -> _)

Scala pattern matching on sequences other than Lists

I have the following code which recursively operates on each element within a List
def doMatch(list: List[Int]): Unit = list match {
case last :: Nil => println("Final element.")
case head :: tail => println("Recursing..."); doMatch(tail)
}
Now, ignoring that this functionality is available through filter() and foreach(), this works just fine. However, if I try to change it to accept any Seq[Int], I run into problems:
Seq doesn't have ::, but it does have +:, which as I understand is basically the same thing. If I try to match on head +: tail however, the compiler complains 'error: not found: value +:'
Nil is specific to List, and I'm not sure what to replace it with. I'm going to try Seq() if I ever get past the previous problem
Here is how I think the code should look, except it doesn't work:
def doMatch(seq: Seq[Int]): Unit = seq match {
case last +: Seq() => println("Final element.")
case head +: tail => println("Recursing..."); doMatch(tail)
}
Edit: So many good answers! I'm accepting agilesteel's answer as his was the first that noted that :: isn't an operator in my example, but a case class and hence the difference.
As of the ides of March 2012, this works in 2.10+:
def doMatch(seq: Seq[Int]): Unit = seq match {
case last +: Seq() => println("Final element.")
case head +: tail => println("Recursing..."); doMatch(tail)
} //> doMatch: (seq: Seq[Int])Unit
doMatch(List(1, 2)) //> Recursing...
//| Final element.
More generally, two different head/tail and init/last decomposition objects mirroring append/prepend were added for Seq in SeqExtractors:
List(1, 2) match { case init :+ last => last } //> res0: Int = 2
List(1, 2) match { case head +: tail => tail } //> res1: List[Int] = List(2)
Vector(1, 2) match { case init :+ last => last } //> res2: Int = 2
Vector(1, 2) match { case head +: tail => tail } //> res3: scala.collection.immutable.Vector[Int] = Vector(2)
Kind of cheating, but here it goes:
def doMatch(seq: Seq[Int]): Unit = seq match {
case Seq(x) => println("Final element " + x)
case Seq(x, xs#_*) => println("Recursing..." + x); doMatch(xs)
}
Don't ask me why xs* doesn't work...
There are two :: (pronounced cons) in Scala. One is an operator defined in class List and one is a class (subclass of List), which represents a non empty list characterized by a head and a tail.
head :: tail is a constructor pattern, which is syntactically modified from ::(head, tail).
:: is a case class, which means there is an extractor object defined for it.
You can actually define an object for +: to do exactly what you are looking for:
object +: {
def unapply[T](s: Seq[T]) =
if(s.nonEmpty)
Some(s.head, s.tail)
else
None
}
scala> val h +: t = Seq(1,2,3)
h: Int = 1
t: Seq[Int] = List(2, 3)
Then your code works exactly as expected.
This works because h +: t is equivalent to +:(h,t) when used for patten matching.
I don't think there is pattern matching support for arbitrary sequences in the standard library. You could do it with out pattern matching though:
def doMatch(seq: Seq[Int]) {
if (seq.size == 1) println("final element " + seq(0)) else {
println("recursing")
doMatch(seq.tail)
}
}
doMatch(1 to 10)
You can define your own extractor objects though. See http://www.scala-lang.org/node/112
object SEQ {
def unapply[A](s:Seq[A]):Option[(A, Seq[A])] = {
if (s.size == 0) None else {
Some((s.head, s.tail))
}
}
}
def doMatch(seq: Seq[Int]) {
seq match {
case SEQ(head, Seq()) => println("final")
case SEQ(head, tail) => {
println("recursing")
doMatch(tail)
}
}
}
A simple tranformation from Seq to List would do the job:
def doMatch (list: List[Int]): Unit = list match {
case last :: Nil => println ("Final element.")
case head :: tail => println ("Recursing..."); doMatch (tail)
case Nil => println ("only seen for empty lists")
}
def doMatchSeq (seq: Seq[Int]) : Unit = doMatch (seq.toList)
doMatch (List(3, 4, 5))
doMatchSeq (3 to 5)