Converting multiple Option[T] to a List[T] - scala

I have several Option[T] that I would like to convert to a List[T]. I'm attempting to achieve this with a for comprehension:
val someOption: Option[(String, String)] = Some(("bla", "woo"))
val anotherOption: Option[String] = None
val result = for {
(a, b) <- someOption
c <- anotherOption
} yield List(a, b, c)
However result in this case becomes of type Option[List[String]] and contains None. How can I instead have result become of type List[String] with a value of List("bla", "woo").
Edit: This is a contrived example, in reality I need to use a and b to instantiate SomeOtherThing(a, b) and have that be a list item.

One way of doing it:
val someOption: Option[(String, String)] = Some(("bla", "woo"))
val anotherOption: Option[String] = None
val someOptionList = for {
(a, b) <- someOption.toList
v <- List(a, b)
} yield v
val result = someOptionList ++ anotherOption.toList
or, to reduce constructions of intermediate collections:
val resultIter = someOption.iterator.flatMap {
case (a, b) => Seq(a,b)
} ++ anotherOption.iterator
val result = resultIter.toList

Hmm, well, the use case is still not absolutely clear to me.
However, here are some thoughts.
The issue with your code is that, in a for comprehension, you always end up with the Collection(like) you start it, so in your case Option.
There is always the possibility to just prepend another Collection to a list with ++:.
So, maybe something like that?:
def addOptionTo(
option: Option[(String, String)],
list: List[SomeOtherThing]
): List[SomeOtherThing] = {
option.map { case (foo, bar) => SomeOtherThing(foo, bar) } ++: list
}

Related

How to unpack Option[(Int, Int)] in Scala

The following is a valid and readable piece of code to unpack returned values.
def func: (Int, Int) = (1, 2)
val (a, b) = func
What about the functions that return Option? For example:
def func2: Option[(Int, Int)] = Some((1, 2))
How can I unpack this in a readable way?
Note that (Int, Int) is sugar for tuple type
Tuple2[Int, Int]
so Option[(Int, Int)] becomes
Option[Tuple2[Int, Int]]
thus correct syntax would be
val Some(Tuple2(a, b)) = func2
or
val Some((a, b)) = func2
or
val Some(a -> b) = func2
However mind if func2 returns None then it will explode with MatchError. The reason becomes clear if we examine the expanded version which is something like
val x: (Int, Int) = func2 match {
case Some((a, b)) => (a, b)
// but what about None case ??
}
val a = x._1
val b = x._2
Note how we did not handle None case. For this reason such extraction is rarely done. Usually we map over the Option and continue working within the context of the Option
func2.map { case (a, b) =>
// work with a and b
}
or we provide some default value if possible
val (a, b) = func2.getOrElse((0, 0))

Concat sorted generic sequence

I need to concat two generic sequences, I tried to do it like this, but I understand that this is wrong. How should I do it in right way? (I need to get new Seq which will be ordered too)
object Main extends App{
val strings = Seq("f", "d", "a")
val numbers = Seq(1,5,4,2)
val strings2 = Seq("c", "b");
val strings3 = strings2.concat(strings)
println(strings3)
println(numbers)
}
class Seq[T] private(initialElems: T*) {
override def toString: String = initialElems.toString
val elems = initialElems
def concat(a:Seq[T]) = a.elems ++ this.elems
}
object Seq {
def apply[T: Ordering](initialElems: T*): Seq[T] = new Seq(initialElems.sorted:_*)
}
You could create a function which will go through both lists taking heads of both lists and comparing them, then appending appropriate head to the result list. Then it should take the next two heads and repeat until one list is over.
Here's tail-recursive example:
import scala.annotation.tailrec
def merge[A](a: List[A], b: List[A])(implicit ordering: Ordering[A]): List[A] = {
#tailrec
def go(a: List[A], b: List[A], acc: List[A] = Nil): List[A] = {
(a, b) match {
case (ax :: as, bx :: bs) => if(ordering.compare(ax, bx) < 0) go(as, bx :: bs, ax :: acc) else go(ax :: as, bs, bx :: acc)
case (Nil, bs) => acc.reverse ++ bs
case (as, Nil) => acc.reverse ++ as
case _ => acc.reverse
}
}
go(a, b)
}
val strings = List("a", "d", "f")
val strings2 = List("b", "c")
merge(strings, strings2) // List(a,b,c,d,e)
I used List instead of Seq. You should rather not use Seq, which is very general type, but utilize more specific collection types, which suit your task best, like Vector, List, ArraySeq etc.
You can't concat two sorted arrays using ++ keeping order. ++ just stick one sequence to the end of another.
You need to implement something like merge operation from merge sort algorithm and create new Seq from merged elems without sorting.
So, you need to do 3 things:
Implement merge:
def merge(a: Seq[T], b: Seq[T]): YourElemsType[T] = ???
Implement new method for creating Seq instance without sorting in object Seq:
def fromSorted(initialElems: T*): Seq[T] = new Seq(initialElems:_*)
After all, your concat can be implemented as composition merge and fromSorted:
def concat(a:Seq[T]): Seq[T] = Seq.fromSorted(merge(this, a))
Read more about merge sort wiki

Transform a Scala Sequence in Pairs

I have a sequence like this:
val l = Seq(1,2,3,4)
which I want to transform to List(Seq(1,2), Seq(2,3), Seq(3,4))
Here is what I tried:
def getPairs(inter: Seq[(Int, Int)]): Seq[(Int, Int)] = l match {
case Nil => inter
case x :: xs => getPairs(inter :+ (x, xs.head))
}
This strangely seems not to work? Any suggestions?
You can also just use sliding:
l.sliding(2).toList
res1: List[Seq[Int]] = List(List(1, 2), List(2, 3), List(3, 4))
Ok I got to know about the zip method:
xs zip xs.tail
Using a for comprehension, for instance as follows,
for ( (a,b) <- l zip l.drop(1) ) yield Seq(a,b)
Note l.drop(1) (in contrast to l.tail) will deliver an empty list if l is empty or has at most one item.
The already given answers describe well, how to do this in a scala way.
However, you might also want an explanation why your code does not work, so here it comes:
Your getPairs function expects a list of tuples as input and returns a list of tuples. But you say you want to transform a list of single values into a list to tuples. So if you call getPairs(l) you will get a type mismatch compiler error.
You would have to refactor your code to take a simple list:
def pairs(in: Seq[Int]): Seq[(Int, Int)] = {
#tailrec
def recursive(remaining: Seq[Int], result: Seq[(Int, Int)]): Seq[(Int, Int)] = {
remaining match {
case Nil => result
case last +: Nil => result
case head +: next +: tail => recursive(next +: tail, (head, next) +: result)
}
}
recursive(in, Nil).reverse
}
and from here it's a small step to a generic function:
def pairs2[A](in: Seq[A]): Seq[(A, A)] = {
#tailrec
def recursive(remaining: Seq[A], result: Seq[(A, A)]): Seq[(A, A)] = {
remaining match {
case Nil => result
case last +: Nil => result
case head +: next +: tail => recursive(next +: tail, (head, next) +: result)
}
}
recursive(in, Nil).reverse
}

How do I find the min() or max() of two Option[Int]

How would you find minValue below?
I have my own solution but want to see how others would do it.
val i1: Option[Int] = ...
val i2: Option[Int] = ...
val defaultValue: Int = ...
val minValue = ?
Update: I just noticed that my solution below and the one in your answer behave differently—I read your question as asking for the minimum of the two values when there are two values, but in your answer you're effectively treating None as if it contained a value that's either bigger (for min) or smaller (for max) than anything else.
To be more concrete: if i1 is Some(1) and i2 is None, my solution will return the default value, while yours will return 1.
If you want the latter behavior, you can use the default semigroup instance for Option[A] and the tropical semigroup for Int. In Scalaz 7, for example, you'd write:
import scalaz._, Scalaz._
optionMonoid(Semigroup.minSemigroup[Int]).append(i1, i2) getOrElse defaultValue
Or the following shorthand:
Tags.Min(i1) |+| Tags.Min(i2) getOrElse defaultValue
It's not as clean as the applicative functor solution below, but if that's your problem, that's your problem.
Here's a more idiomatic way that doesn't involve creating an extra list:
(for { x <- i1; y <- i2 } yield math.min(x, y)) getOrElse defaultValue
Or, equivalently:
i1.flatMap(x => i2.map(math.min(x, _))) getOrElse defaultValue
What you're doing is "lifting" a two-place function (min) into an applicative functor (Option). Scalaz makes this easy with its applicative builder syntax:
import scalaz._, Scalaz._
(i1 |#| i2)(math.min) getOrElse defaultValue
The standard library solution isn't much less elegant in this case, but this is a useful abstraction to know about.
I solved a similar problem using the following approach. We handle a special case when both of the options have values, otherwise we use an API method Option.orElse.
val a: Option[Int] = Some(10)
val b: Option[Int] = Some(20)
val c: Option[Int] = (a, b) match {
case (Some(x), Some(y)) => Some(x min y)
case (x, y) => x orElse y
}
I think this is what you're after:
val minValue = List(i1, i2).flatten match {
case Nil => defaultValue
case xs => xs.min
}
I'd avoid sorted since sorting requires a lot more processing than simply finding the max or min (although it probably doesn't make much difference in this case).
val minValue: Int = List(i1, i2).flatten.sorted.headOption getOrElse defaultValue
You can use patterns in for expressions, values that do not match the pattern are discarded.
(for (Some(x) <- List(None, Some(3))) yield x) max
Not as good as the List.flatten approach though.
Another option which wasn't mentioned is using reduceLeftOption (interchange math.max and math.min as desired):
val min = (first ++ second).reduceLeftOption(math.min).getOrElse(defaultValue)
scala> val first = Some(10)
first: Some[Int] = Some(10)
scala> val second: Option[Int] = None
second: Option[Int] = None
scala> val defaultMin = -1
defaultMin: Int = -1
scala> (first ++ second).reduceLeftOption(math.min).getOrElse(defaultMin)
res7: Int = 10
scala> val first: Option[Int] = None
first: Option[Int] = None
scala> (first ++ second).reduceLeftOption(math.min).getOrElse(defaultMin)
res8: Int = -1
scala> val first = Some(10)
first: Some[Int] = Some(10)
scala> val second = Some(42)
second: Some[Int] = Some(42)
scala> (first ++ second).reduceLeftOption(math.min).getOrElse(defaultMin)
res9: Int = 10
We can combine the 2 Options as an Iterable with Option's ++ operator, which allows us to use minOption (to nicely handle the case of the empty iterable formed by the None/None case) and fallback on a default value if necessary with getOrElse:
(optionA ++ optionB).minOption.getOrElse(-1)
// None and None => -1
// Some(5) and None => 5
// None and Some(5) => 5
// Some(5) and Some(3) => 3
If you want to avoid using scalaz and map/for/getOrElse, you can do the following:
val minValue = (i1, i2) match {
case (Some(x), Some(y)) => math.min(x, y)
case _ => defaultValue
}
tl;dr
You can do that you need elegant using custom cats Semigroup instances:
import cats.kernel.Semigroup
import cats.instances.option._ // this import is for cats std option combiner
import cats.syntax.semigroup._
object Implicits {
implicit val intMinSemigroup: Semigroup[Int] =
(x: Int, y: Int) => math.min(x, y)
implicit val intMaxSemigroup: Semigroup[Int] =
(x: Int, y: Int) => math.max(x, y)
}
import Implicits.intMinSemigroup
// these are results for minSemigroup
// List((Some(1),Some(1),Some(2)), (Some(1),Some(1),None), (None,Some(2),Some(2)), (None,None,None))
//import Implicits.intMaxSemigroup
// these are results for maxSemigroup
// List((Some(1),Some(2),Some(2)), (Some(1),Some(1),None), (None,Some(2),Some(2)), (None,None,None))
for {
maybeA <- Seq(Some(1), None)
maybeB <- Seq(Some(2), None)
} yield (maybeA, maybeA |+| maybeB, maybeB)
if you want replace None by default value you can use combine twice:
val defaultValue: Int = 3
val optionMin = for {
maybeA <- Seq(Some(1), None)
maybeB <- Seq(Some(2), None)
} yield (maybeA |+| maybeB) |+| Some(defaultValue)
// List(Some(1), Some(1), Some(2), Some(3))
How it works
Shortly, Semigroup[A] is typeclass for combining two values of the same type A into the one value of type A.
Here we use std cats OptionMonoid (it extends Semigroup[Option[A]]) here source code:
class OptionMonoid[A](implicit A: Semigroup[A]) extends Monoid[Option[A]] {
def empty: Option[A] = None
def combine(x: Option[A], y: Option[A]): Option[A] =
x match {
case None => y
case Some(a) =>
y match {
case None => x
case Some(b) => Some(A.combine(a, b))
}
}
}
We see that it takes option matching on his own and everything what we should give him to work is implicit A: Semigroup[A]. In our case we write two different combiners for min, max cases:
object Implicits {
implicit val intMinSemigroup: Semigroup[Int] =
(x: Int, y: Int) => math.min(x, y)
implicit val intMaxSemigroup: Semigroup[Int] =
(x: Int, y: Int) => math.max(x, y)
}
So, we import combiners (i.e. import Implicits.intMinSemigroup) and just use cats.syntax.semigroup for using combine function as operator |+|:
maybeA |+| maybeB.
In conclusion, you can just define your custom semigroup for any type (not only Int) and combine options of this type after importing some cats syntax and instances.

How to create a list with the same element n-times?

How to create a list with the same element n-times ?
Manually implementnation:
scala> def times(n: Int, s: String) =
| (for(i <- 1 to n) yield s).toList
times: (n: Int, s: String)List[String]
scala> times(3, "foo")
res4: List[String] = List(foo, foo, foo)
Is there also a built-in way to do the same ?
See scala.collection.generic.SeqFactory.fill(n:Int)(elem: =>A) that collection data structures, like Seq, Stream, Iterator and so on, extend:
scala> List.fill(3)("foo")
res1: List[String] = List(foo, foo, foo)
WARNING It's not available in Scala 2.7.
(1 to n).map( _ => "foo" )
Works like a charm.
Using tabulate like this,
List.tabulate(3)(_ => "foo")
I have another answer which emulates flatMap I think (found out that this solution returns Unit when applying duplicateN)
implicit class ListGeneric[A](l: List[A]) {
def nDuplicate(x: Int): List[A] = {
def duplicateN(x: Int, tail: List[A]): List[A] = {
l match {
case Nil => Nil
case n :: xs => concatN(x, n) ::: duplicateN(x, xs)
}
def concatN(times: Int, elem: A): List[A] = List.fill(times)(elem)
}
duplicateN(x, l)
}
}
def times(n: Int, ls: List[String]) = ls.flatMap{ List.fill(n)(_) }
but this is rather for a predetermined List and you want to duplicate n times each element