Mapping values from a List of Either to List - scala

I have two lists one is:
val l1 = List[Either[A,B]]
and
val l2 = List[String]
Now, all I need to do is map these two. i.e., if l1 is A then return corresponding value from l2. Tried something like:
for {
elem1 <- l1
elem2 <- l2
result <- if(elem1.isLeft) url
} yield result
This doesn't work. Because, I am not handling the else case. Similarly with match instead of if. How do I go about to achieve this?

You could do something like this (I'm assuming l2 has at least the same number of elements of type A as Lefts in l1):
val result: List[String] = l1.zip(l2).filter(_._1.isLeft).map(_._2)
Otherwise, if you prefer using for, this will also do the trick:
scala> for {
| e1 <- l1.zip(l2)
| if e1._1.isLeft
| } yield e1._2

Related

Scala - conditional product/join of two arrays with default values using for comprehensions

I have two Sequences, say:
val first = Array("B", "L", "T")
val second = Array("T70", "B25", "B80", "A50", "M100", "B50")
How do I get a product such that elements of the first array are joined with each element of the second array which startsWith the former and also yield a default empty result when no element in the second array meets the condition.
Effectively to get an Output:
expectedProductArray = Array("B-B25", "B-B80", "B-B50", "L-Default", "T-T70")
I tried doing,
val myProductArray: Array[String] = for {
f <- first
s <- second if s.startsWith(f)
} yield s"""$f-$s"""
and i get:
myProductArray = Array("B-B25", "B-B80", "B-B50", "T-T70")
Is there an Idiomatic way of adding a default value for values in first sequence not having a corresponding value in the second sequence with the given criteria? Appreciate your thoughts.
Here's one approach by making array second a Map and looking up the Map for elements in array first with getOrElse:
val first = Array("B", "L", "T")
val second = Array("T70", "B25", "B80", "A50", "M100", "B50")
val m = second.groupBy(_(0).toString)
// m: scala.collection.immutable.Map[String,Array[String]] =
// Map(M -> Array(M100), A -> Array(A50), B -> Array(B25, B80, B50), T -> Array(T70))
first.flatMap(x => m.getOrElse(x, Array("Default")).map(x + "-" + _))
// res1: Array[String] = Array(B-B25, B-B80, B-B50, L-Default, T-T70)
In case you prefer using for-comprehension:
for {
x <- first
y <- m.getOrElse(x, Array("Default"))
} yield s"$x-$y"

Confused about behavior on Option between single-level and nested for comprehension

I'm new to Scala so please bear with me.
I'm confused about the behaviors below:
val l = List(Option(1))
for (i <- l; x <- i) yield x //Example 1: gives me List(1)
for(x <- Option(1)) yield x //Example 2: gives me Some(1)
Why doesn't the second for comprehension give me 1 instead? Because that would look more consistent to me, intuitively, since the second for comprehension in the first example x <- i looks like it should behave exactly the same way as the second example, as the second example basically has extracted the option out of the list to begin with.
Simply put, for comprehension wraps into the type that was used the first time.
for (x <- Option(1)) yield x // Returns Option
for (x <- List(1)) yield x // Returns List
for (x <- Array(1)) yield x // Returns Array
This:
for (i <- List(Some(1)); x <- i) yield x
Desugares into this:
List(Some(1)).flatMap { case i => i.map { case x => x } }
flatMap of List returns List[T], that's why it behaves like that

Generating iterator instead of list in recursion

There is a good question which says that we should Stream/View or Iterate our collections to make them on-demand. It is clear. I just do not understand what should I apply the .view or .iterate to in the following demo
val l1 = List("10", "00", "0")
def gen(depth: Int): Iterable[String] = if (depth == 1) l1 else {
for (sub <- gen(depth-1); item <- List(depth + sub, sub+sub, sub)) yield item
}
Should I apply them to gen(depth-1) or to List(depth+..)?
By the way, should I inline l1 in the (depth == 1) l1 else? It is not used anywhere else. I just afraid that it would create a new list for every leaf.
I would have to try it, but I would say List(depth+..).view, since you wont use the same values more than once.I think it will give you a sort of DFS traversal given the recursion you have there.
And yeah, I would not inline it but live l1 like that. I would even do val l1 = List("10", "00", "0").view.

Scala list of tuples of different size zip issues?

Hi my two lists as follows:
val a = List((1430299869,"A",4200), (1430299869,"A",0))
val b = List((1430302366,"B",4100), (1430302366,"B",4200), (1430302366,"B",5000), (1430302366,"B",27017), (1430302366,"B",80), (1430302366,"B",9300), (1430302366,"B",9200), (1430302366,"A",5000), (1430302366,"A",4200), (1430302366,"A",80), (1430302366,"A",443), (1430302366,"C",4100), (1430302366,"C",4200), (1430302366,"C",27017), (1430302366,"C",5000), (1430302366,"C",80))
when I used zip two lists as below :
val c = a zip b
it returns results as
List(((1430299869,A,4200),(1430302366,B,4100)), ((1430299869,A,0),(1430302366,B,4200)))
Not all lists of tuples, how can I zip all above data?
EDIT
expected results as combine of two lists like :
List((1430299869,"A",4200), (1430299869,"A",0),(1430302366,"B",4100), (1430302366,"B",4200), (1430302366,"B",5000), (1430302366,"B",27017), (1430302366,"B",80), (1430302366,"B",9300), (1430302366,"B",9200), (1430302366,"A",5000), (1430302366,"A",4200), (1430302366,"A",80), (1430302366,"A",443), (1430302366,"C",4100), (1430302366,"C",4200), (1430302366,"C",27017), (1430302366,"C",5000), (1430302366,"C",80))
Second Edit
I tried this :
val d = for(((a,b,c),(d,e,f)) <- (a zip b)if(b.equals(e) && c.equals(f))) yield (d,e,f)
but it gives empty results because of (a zip b) but I replaced a zip b as a ++ b then it shows following error :
constructor cannot be instantiated to expected type;
So how can I get matching tuples?
Just add one list to another:
a ++ b
According to your 2nd edit, what you need is:
for {
(a1,b1,c) <- a //rename extracted to a1 and b1 to avoid confusion
(d,e,f) <- b
if b1.equals(e) && c.equals(f)
} yield (d,e,f)
Or:
for {
(a1, b1, c) <- a
(d, `b1`, `c`) <- b //enclosing it in backticks avoids capture and matches against already defined values
} yield (d, b1, c)
Zipping won't help since you need to compare all tuples in a with all tuples in b , it seems.
a zip b creates a list of pairs of elements from a and b.
What you're most likely looking for is list concatenation, which is a ++ b
On zipping (pairing) all data in the lists, consider first a briefer input for illustrating the case,
val a = (1 to 2).toList
val b = (10 to 12).toList
Then for instance a for comprehension may convey the needs,
for (i <- a; j <- b) yield (i,j)
which delivers
List((1,10), (1,11), (1,12),
(2,10), (2,11), (2,12))
Update
From OP latest update, consider a dedicated filtering function,
type triplet = (Int,String,Int)
def filtering(key: triplet, xs: List[triplet]) =
xs.filter( v => key._2 == v._2 && key._3 == v._3 )
and so apply it with flatMap,
a.flatMap(filtering(_, b))
List((1430302366,A,4200))
One additional step is to encapsulate this in an implicit class,
implicit class OpsFilter(val keys: List[triplet]) extends AnyVal {
def filtering(xs: List[triplet]) = {
keys.flatMap ( key => xs.filter( v => key._2 == v._2 && key._3 == v._3 ))
}
}
and likewise,
a.filtering(b)
List((1430302366,A,4200))

Converting nested for loops into for comprehension

I can collect the results at the inner-most for body into a List[Output] and return them. But I want to use yield. How can this method be converted into using for-yield pattern:
def useForComprehension(input : Input): List[Output] = {
for (o <- splitInputIntoPieces(input)) {
for (restResults <- useForComprehension(subtract(input, o)) ) {
for (w <- f3(o)) {
yield w::restResults // !!!!! Error
}
}
}
}
In Scala, nested iteration is handled by adding additional <- clauses.
For example, let's say we have two lists l1 and l2 and we want generate over every pair of elements (x,y) where x is in l1 and y is in l2. The syntax in Scala is:
for { x <- l1
y <- l2
} yield (x,y)
When no yield keyword follows the for then the entire expression results in a type of Unit, which is the source of your type error. This is useful for performing side effects on the iteration, for example
for { x <- l1
y <- l2
} println((x,y))
For more information on for comprehensions see What is Scala's yield?
Your error may be because you are surrounding yield in {}.
for {stuff} {yield otherstuff}
The form should be:
for {stuff} yield otherstuff
You can of course replace "otherstuff" with a block if you want it to contain multiple expressions so you have:
for {stuff} yield {otherstuff}
Using your example I suspect you want something like:
def useForComprehension(input: Input): List[Output] =
for {
o <- splitInputIntoPieces(input)
restResults <- useForComprehension(subtract(input, o))
w <- f3(o)
} yield w :: restResults