For comprehension with Map lookup - is there a better way? - scala

Consider the following
val myMap: Map[String, List[Int]] = Map("a" -> List(1,2,3),
"b" -> List(4,5,6),
"d" -> List(7))
val possibleKeys: List[String] = List("c","a", "b", "e")
I would like to traverse the possible keys, and if the map contains one, traverse the map's values
The options I came up with are:
With a filter
for {
key <- possibleKeys
if (myMap contains key)
int <- myMap(key)
r <- 0 to int
} yield (r, int)
With getOrElse
for {
key <- possibleKeys
int <- myMap.getOrElse(key, Nil)
r <- 0 to int
} yield (r, int)
(Both return the same result:)
List((0,1), (1,1), (0,2), (1,2), (2,2), (0,3), (1,3), (2,3), (3,3), (0,4), (1,4), (2,4), (3,4), (4,4), (0,5), (1,5), (2,5), (3,5), (4,5), (5,5), (0,6), (1,6), (2,6), (3,6), (4,6), (5,6), (6,6))
Since I know Scala supports Options in for comprehensions, I was a bit surprised that this didn't work
for {
key <- possibleKeys
int <- myMap.get(key)
r <- 0 to int //<-- compilation error
} yield (r, int)
It complains about type mismatch; found : List[Int] required: Int
Which I vaguely understand why, but is there a way to make this work without the if clause or the getOrElse methods? (e.g. is there a way to get the myMap.get(key) version to work?)

You are trying to mix incompatible types in your for comprehension. You can fix it by converting the option to a Seq by example.
for {
key <- possibleKeys
ints <- myMap.get(key).toSeq
int <- ints
r <- 0 to int
} yield (r, int)
There is a rather nice explanation of the problem in this very similar question here: Type Mismatch on Scala For Comprehension.

You can make use of the methods keySet and apply on Map
for {
key <- possibleKeys
if myMap.keySet(key)
int <- myMap(key)
r <- 0 to int
} yield (r, int)
res5: List[(Int, Int)] = List((0,1), (1,1), (0,2), (1,2), (2,2), (0,3), (1,3), (2,3), (3,3), (0,4), (1,4), (2,4), (3,4), (4,4), (0,5), (1,5), (2,5), (3,5), (4,5), (5,5), (0,6), (1,6), (2,6), (3,6), (4,6), (5,6), (6,6))

Related

How can I split a list of tuples scala

I have this list in Scala (which in reality has length 500):
List((1,List(1,2,3)), (2,List(1,2,3)), (3, List(1,2,3)))
What could I do so that I can make a new list which contains the following:
List((1,1), (1,2), (1,3), (2,1), (2,2), (2,3), (3,1), (3,2), (3,3))
Basically I wanna have a new list of tuples which will contain the first element of the old tuple and each element of the list inside the tuple. I am not sure how to start implementing this and this is why I have posted no code to show my attempt. I am really sorry, but I cant grasp this. I appreciate any help you can provide.
Exactly the same as #Andriy but using a for comprehension.
Which in the end is exactly the same but is more readable IMHO.
val result = for {
(x, ys) <- xs
y <- ys
} yield (x, y) // You can also use x -> y
(Again, I would recommend you to follow any tutorial, this is a basic exercise which if you had understand how map & flatMap works you shouldn't have any problem)
scala> val xs = List((1,List(1,2,3)), (2,List(1,2,3)), (3, List(1,2,3)))
xs: List[(Int, List[Int])] = List((1,List(1, 2, 3)), (2,List(1, 2, 3)), (3,List(1, 2, 3)))
scala> xs.flatMap { case (x, ys) => ys.map(y => (x, y)) }
res0: List[(Int, Int)] = List((1,1), (1,2), (1,3), (2,1), (2,2), (2,3), (3,1), (3,2), (3,3))
It's probably worth mentioning that the solution by Andriy Plokhotnyuk can also be re-written as a for-comprehension:
val list = List((1,List(1,2,3)), (2,List(1,2,3)), (3, List(1,2,3)))
val pairs = for {
(n, nestedList) <- list
m <- nestedList
} yield (n, m)
assert(pairs == List((1,1), (1,2), (1,3), (2,1), (2,2), (2,3), (3,1), (3,2), (3,3)))
The compiler will effectively re-write the for-comprehension to a flatMap/map chain as described in another answer.

How to access second element in a Sequence in scala

val k = Seq((0,1),(1,2),(2,3),(3,4))
k: Seq[(Int, Int)] = List((0,1), (1,2), (2,3), (3,4))
If I have above statement and I need to do addition for even places and subtraction for odd places how can I access them? to be clear
(0,1) has to become (0,(1+2))
(1,2) has to become (1,(1-2))
(2,3) has to become (2,(3+4))
(3,4) has to become (3,(3-4)
Do you mean something like this?
val transformed = k.grouped(2).flatMap{
case Seq((i, x), (j, y)) => Seq((i, x + y), (j, x - y))
}
transformed.toList
// List[(Int, Int)] = List((0,3), (1,-1), (2,7), (3,-1))

How to convert List[Any] to List(Int,Int) in scala?

I have List[Any] which has values
List((0,0), (1,1), (1,3), (2,2), (3,1))
I want to convert it into
List[(Int,Int)]
How can I do this?
You can use collect method, so that it will not fail in case List contains not only tuples of Ints
val l: List[Any] = List((0,0), (1,1), (1,3), (2,2), (3,1))
l.collect{ case (a: Int, b: Int) => (a,b)}
You can just cast it using asInstanceOf.
scala> val a: List[Any] = List((0,0), (1,1), (1,3), (2,2), (3,1))
a: List[Any] = List((0,0), (1,1), (1,3), (2,2), (3,1))
scala> a.asInstanceOf[List[(Int, Int)]]
res0: List[(Int, Int)] = List((0,0), (1,1), (1,3), (2,2), (3,1))
Though this is dangerous because of partial erasure. See how it can create heap pollution:
scala> val a: List[Any] = List((0,0), (1,1), "abc")
a: List[Any] = List((0,0), (1,1), abc)
scala> a.asInstanceOf[List[(Int, Int)]]
res0: List[(Int, Int)] = List((0,0), (1,1), abc)
So do this only if performance is a big concern here. Otherwise, I prefer one of the other two answers (summarized below).
If you want failure in the form of ClassCastException(#mattinbits's answer):
a.map(_.asInstanceOf[(Int, Int)])
If you want failures to be passed over silently (#grotrianster's answer):
a.collect({ case (a: Int, b: Int) => (a, b) })
If you know that the type of every member will be (Int, Int), then just use asInstanceOf:
val l:List[Any] = List((0,0), (1,1), (1,3), (2,2), (3,1))
l.map(_.asInstanceOf[(Int, Int)])
Using a for comprehension, like this
for ( (a:Int,b:Int) <- xs ) yield (a,b)
This will quietly skim pairs whose type signature do not match [(Int,Int)].

How to return Cartesian of List elements

I have List of 3 elements 1,2,3 :
val s : List[String] = List("1" , "2" , "3") //> s : List[String] = List(1, 2, 3)
When I attempt to extract a cartesian of these elements :
s.map(m => m.map(m2 => (m,m2))) //> res0: List[scala.collection.immutable.IndexedSeq[(String, Char)]] = List(Vec
//| tor((1,1)), Vector((2,2)), Vector((3,3)))
Why is Cartesian product of list elements not being returned ?
s.map(m => m.map(m2 => (m,m2)))
^ This is a String, which is being treated as a Seq[Char]
s.map(m => m.map(m2 => (m,m2)))
^ This is a Char in the String, of which there is only one in each.
It's easier to see how this fails if the Strings are longer.
scala> val s : List[String] = List("1a" , "2b" , "3c")
s: List[String] = List(1a, 2b, 3c)
scala> s.map(m => m.map(m2 => (m,m2)))
res1: List[scala.collection.immutable.IndexedSeq[(String, Char)]] = List(Vector((1a,1), (1a,a)), Vector((2b,2), (2b,b)), Vector((3c,3), (3c,c)))
The inner map needs to use the same seed list:
scala> for(a <- s; b <- s) yield (a, b)
res7: List[(Int, Int)] = List((1,1), (1,2), (1,3), (2,1), (2,2), (2,3), (3,1), (3,2), (3,3))
Or:
scala> s.flatMap(a => s.map(b => (a, b)))
res8: List[(Int, Int)] = List((1,1), (1,2), (1,3), (2,1), (2,2), (2,3), (3,1), (3,2), (3,3))

Can I use for-comprehenion / yield to create a map in Scala?

Can I "yield" into a Map?
I've tried
val rndTrans = for (s1 <- 0 to nStates;
s2 <- 0 to nStates
if rnd.nextDouble() < trans_probability)
yield (s1 -> s2);
(and with , instead of ->) but I get the error
TestCaseGenerator.scala:42: error: type mismatch;
found : Seq.Projection[(Int, Int)]
required: Map[State,State]
new LTS(rndTrans, rndLabeling)
I can see why, but I can't see how to solve this :-/
scala> (for(i <- 0 to 10; j <- 0 to 10) yield (i -> j)) toMap
res1: scala.collection.immutable.Map[Int,Int] = Map((0,10), (5,10), (10,10), (1,10), (6,10), (9,10), (2,10), (7,10), (3,10), (8,10), (4,10))
An alternate solution in Scala 2.8:
Welcome to Scala version 2.8.1.r23457-b20101106033551 (Java HotSpot(TM) Client VM, Java 1.6.0_22).
Type in expressions to have them evaluated.
Type :help for more information.
scala> import scala.collection.breakOut
import scala.collection.breakOut
scala> val list: List[(Int,Int)] = (for(i<-0 to 3;j<-0 to 2) yield(i->j))(breakOut)
list: List[(Int, Int)] = List((0,0), (0,1), (0,2), (1,0), (1,1), (1,2), (2,0), (2,1), (2,2), (3,0), (3,1), (3,2))
scala> val map: Map[Int,Int] = (for(i<-0 to 3;j<-0 to 2) yield(i->j))(breakOut)
map: Map[Int,Int] = Map((0,2), (1,2), (2,2), (3,2))
scala> val set: Set[(Int,Int)] = (for(i<-0 to 3;j<-0 to 2) yield(i->j))(breakOut)
set: Set[(Int, Int)] = Set((2,2), (3,2), (0,1), (1,2), (0,0), (2,0), (3,1), (0,2), (1,1), (2,1), (1,0), (3,0))
scala>
Alternative (works on 2.7):
scala> Map((for(i <- 0 to 10; j <- 0 to 10) yield (i -> j)): _*)
res0: scala.collection.immutable.Map[Int,Int] = Map((0,10), (5,10), (10,10), (1,10), (6,10), (9,10), (2,10), (7,10), (3,10), (8,10), (4,10))
val rndTrans = (
for {
s1 <- 0 to nStates
s2 <- 0 to nStates if rnd.nextDouble() < trans_probability
} yield s1 -> s2
) (collection.breakOut[Any, (Int, Int), Map[Int, Int]])