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

4

23

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 :-/

Pythia answered 18/11, 2010 at 10:56 Comment(0)
H
22
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))
Hays answered 18/11, 2010 at 11:1 Comment(7)
Hmm, probably close to what I'm looking for, but I get: error: value toMap is not a member of Seq.Projection[(Int, Int)]Pythia
Thats strange. Which version of Scala are you using?Pythia
Seq.Projection is deprecated in 2.8; so it should not be the result of a built-in for loop. Could you check your version again? (println(util.Properties.versionString))Gene
Yeah. I'm on 2.7 :-( when will the Ubuntu repos be updated to provide 2.8?? Thanks anyway...Pythia
@Pythia FWIW, it's fairly easy to get a later version of scala running in Ubuntu without using the Ubuntu repos. You can a) install the package for your favorite IDE, b) install SBT which will automatically download Scala, or c) download the debian package. See scala-lang.org/node/292#distributions and/or Daniel's answer to this question: #4161960.Newhall
Awsome! Thanks! However, I still can't get sbt working. See my other question.Pythia
@Pythia There's a ticket on Debian about upgrading the Scala package. I hope they do get to it.Quadrisect
P
13

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> 
Postpositive answered 18/11, 2010 at 12:0 Comment(2)
+1 for the ever mysterious (but useful!) breakOut. Correct me if I'm wrong, but I think it affords slightly better performance because it provides a builder that allows direct generation of the map (instead of building another collection which is converted afterward).Ugrian
@Zwirb You are correct. And +1 because I didn't know you could use it with for/yield syntax! :-)Quadrisect
T
5

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))
Thetos answered 18/11, 2010 at 11:16 Comment(1)
Great. I'm stuck with 2.7 for now, so I'll use this one.Pythia
S
1
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]])
Staciastacie answered 22/11, 2016 at 9:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.