Optional tuples to Map in Scala - scala

If I have a List of tuples, I can convert to a map with toMap:
val x = (3 -> 3)
List(x).toMap
and I get
scala.collection.immutable.Map[Int,Int] = Map(3 -> 3)
If I have a List of Optional and try the same I would get an error:
val x = Some(3 -> 3)
val y = None
List(x, y).toMap
<console>:15: error: Cannot prove that Some[(Int, Int)] <:< (T, U).
I want to get the same result. Is it possible?

You can use flatten on the List to remove the Nones:
val x = Some(3 -> 3)
val y = None
List(x, y).flatten.toMap
> scala.collection.immutable.Map[Int,Int] = Map(3 -> 3)

Related

How to group by keys in a list

I have a list in Scala that I want to group by a key and sum up the values of each key:
val l = List(("abc",1),("abc",2),("cbe",5),("cab",1))
I tried this code:
l.groupBy(identity).mapValues(_.sum)
But got the following error:
error: type mismatch;
found : scala.collection.immutable.Map[(String, Int),Int]
required: Seq[(String, Int)]
It might have been already answered in stackoverflow, but you group and then sum the values of list.
scala> val l = List(("abc",1),("abc",2),("cbe",5),("cab",1))
.groupBy(_._1)
.map { case (k, v) => k -> v.map { _._2}.sum}
l: scala.collection.immutable.Map[String,Int] = HashMap(cbe -> 5, abc -> 3, cab -> 1)
Given List:
val list = List(("abc",1),("abc",2),("cbe",5),("cab",1))
Using,
list.groupBy(_._1).mapValues(_.map(_._2).sum)
In Scala REPL:
scala> list.groupBy(_._1).mapValues(_.map(_._2).sum)
res13: scala.collection.immutable.Map[String,Int] = Map(cab -> 1, abc -> 3, cbe -> 5)
Can also be done with groupMapReduce
scala> val l = List(("abc",1),("abc",2),("cbe",5),("cab",1))
.groupMapReduce(_._1)(_._2)(_ + _)
val l: scala.collection.immutable.Map[String,Int] = Map(abc -> 3, cab -> 1, cbe -> 5)

How to set value in scala Map?

I am new to scala. I have a Map. I want to set a value in the Map with a particular key. Here is the code I am writing -
var mp: Map[Int, ParticipationStateTransition] = Map.empty[Int, ParticipationStateTransition]
val change: ParticipationStateTransition = new ParticipationStateTransition
mp(ri.userID) = change
The error it is showing me on the third line is -
application does not take parameters
What am I doing wrong? Thanks in advance.
Use .updated :
scala> val m = Map(1 -> 2)
m: scala.collection.immutable.Map[Int,Int] = Map(1 -> 2)
scala> val n = m.updated(1, 3)
n: scala.collection.immutable.Map[Int,Int] = Map(1 -> 3)
scala> m
res0: scala.collection.immutable.Map[Int,Int] = Map(1 -> 2)
scala> n
res1: scala.collection.immutable.Map[Int,Int] = Map(1 -> 3)
Note that scala's Map are immutable, so you need to assign the return value of .updated, it will not change the original map.
If you want to change the map in place, you can use collection.mutable.Map and then
scala> val m = collection.mutable.Map(1 -> 2)
m: scala.collection.mutable.Map[Int,Int] = Map(1 -> 2)
scala> m.update(1, 3)
scala> m
res3: scala.collection.mutable.Map[Int,Int] = Map(1 -> 3)
If you want to set multiple values at once, you can do :
scala> val m = Map(1 -> 2)
m: scala.collection.immutable.Map[Int,Int] = Map(1 -> 2)
scala> val n = m ++ List((1 -> 3), (2 -> 4)) // also accepts an Array, a Map, …
n: scala.collection.immutable.Map[Int,Int] = Map(1 -> 3, 2 -> 4)

Why I've got type mismatch exception?

I got a weird exception in this snippet
val splitted = "This is a text that to test something".split("\\,|\\ ")
val map = mutable.Map[Int, List[String]]()
(0 to splitted.length).foreach {
case i =>map(i) = map.getOrElse(i,List[String]("")) ++ splitted(i)
}
but I have got this exception:
type mismatch;
found : List[Any]
required: List[String]
case i =>map(i) = map.getOrElse(i,List("")) ++ splitted(i)
^ ^
You are yet another victim of the inference of Any.
scala> map.getOrElse(7,List[String](""))
res3: List[String] = List("")
scala> res3 ++ "abc"
res4: List[Any] = List("", a, b, c)
Here's the same thing with -Xlint:
scala> (0 to splitted.length).foreach {
case i =>map(i) = map.getOrElse(i,List[String]("")) ++ splitted(i)
}
<console>:12: error: type mismatch;
found : List[Any]
required: List[String]
case i =>map(i) = map.getOrElse(i,List[String]("")) ++ splitted(i)
^
scala> val res3 = { map.getOrElse(7,List[String]("")) }
<console>:6: warning: Unused import
import collection.mutable
^
res3: List[String] = List("")
scala> res3 ++ "abc"
<console>:11: warning: a type was inferred to be `Any`; this may indicate a programming error.
res3 ++ "abc"
^
res1: List[Any] = List("", a, b, c)
Unfortunately, the -Xlint warning is smothered by the error.
And, under -Xlint -Xfatal-warnings, the warning doesn't get a chance to error out:
scala> (0 to splitted.length).foreach {
case i =>map(i) = map.getOrElse(i,List[String]("")) ++ splitted(i)
}
<console>:11: error: type mismatch;
found : List[Any]
required: List[String]
case i =>map(i) = map.getOrElse(i,List[String]("")) ++ splitted(i)
^
If the warning is not inside the closure, then you get the helpful message under -Xlint -Xfatal-warnings:
val ss: List[String] = (null: mutable.Map[Int,List[String]]).getOrElse(0,List.empty[String]) ++ "abc"
splitted(i) is a String and ++ (which expects a collection) treats String like Array[Char], which then results in the common supertype List[Any].
Example:
val list = List("a", "b") ++ "hello"
println(list) //prints List(a, b, h, e, l, l, o) and is of type List[Any]
If it doesn't matter where it's inserted then i would do it like this:
case i => map(i) = splitted(i) :: map.getOrElse(i,List[String](""))
If it should be appended:
case i => map(i) = map.getOrElse(i,List[String]("")) :+ splitted(i)
Not sure what you're trying to do with that code but here's a working version:
(0 to splitted.length-1).foreach {
case i =>map(i) = (map.getOrElse(i,List[String]("")) :+ splitted(i))
}
which will create:
println(map)
Map(2 -> List(, a), 5 -> List(, to), 4 -> List(, that), 7 -> List(, something), 1 -> List(, is), 3 -> List(, text), 6 -> List(, test), 0 -> List(, This))
EDIT: Maybe what you need is the following:
splitted.zipWithIndex.map{case (s,i)=>(i,s)}.toMap
which results in:
Map(0 -> This, 5 -> to, 1 -> is, 6 -> test, 2 -> a, 7 -> something, 3 -> text, 4 -> that)
or more efficiently:
((0 to splitted.length-1) zip splitted).toMap
This works.
val splitted = "This is a text that to test something".split("\\,|\\ ")
val map = scala.collection.mutable.Map[Int, List[String]]()
(0 until splitted.length).foreach {
case i =>map(i) = map.getOrElse(i,List[String]("")) :+ splitted(i)
}
Which outputs:
scala.collection.mutable.Map[Int,List[String]] = Map(2 -> List("", a), 5 -> List("", to), 4 -> List("", that), 7 -> List("", something), 1 -> List("", is), 3 -> List("", text), 6 -> List("", test), 0 -> List("", This))
Also, note the difference between to and until. Using to in will give and ArrayOutOfBoundsException.
scala> val l = List(1,2,3)
l: List[Int] = List(1, 2, 3)
scala> 0 to l.length
res51: scala.collection.immutable.Range.Inclusive = Range(0, 1, 2, 3)
scala> 0 until l.length
res52: scala.collection.immutable.Range = Range(0, 1, 2)

Why Scala REPL shows tuple type for Map expression?

Scala REPL gives the same type for both expressions - (tuple? -- strange!). Yet ("a" ->1) which is a Map I can add to map and ("a", 1)can not. Why Scala REPL shows tuple type type for Map expression?
scala> :t ("a" -> 1)
(String, Int)
scala> :t ("a",1)
(String, Int)
scala> val m = Map.empty[String, Int]
m: scala.collection.immutable.Map[String,Int] = Map()
scala> m + ("a",1)
<console>:9: error: type mismatch;
found : String("a")
required: (String, ?)
m + ("a",1)
^
scala> m + ("a" ->1)
res19: scala.collection.immutable.Map[String,Int] = Map(a -> 1)
Scala thinks a + (b,c) means you are trying to call the + method with two arguments, which is a real possibility since maps do have a multi-argument addition method so you can do things like
m + (("a" -> 1), ("b" -> 2))
the solution is simple: just add an extra set of parentheses so it's clear that (b,c) is in fact a tuple being passed as a single argument.
m + (("a", 1))
Actually, the reason for this is that Predef: http://www.scala-lang.org/api/current/index.html#scala.Predef$ (which is always in scope in Scala) contains an implicit conversion from Any to ArrowAssoc (the method implicit def any2ArrowAssoc[A](x: A): ArrowAssoc[A])
ArrowAssoc contains the method -> which converts it to a tuple.
So basically you are doing any2ArrowAssoc("a").->(1) which returns ("a",1).
From repl:
any2ArrowAssoc("a").->(1)
res1: (java.lang.String, Int) = (a,1)
Furthermore, you can work on immutable hashmaps like this:
val x = HashMap[Int,String](1 -> "One")
x: scala.collection.immutable.HashMap[Int,String] = Map((1,One))
val y = x ++ HashMap[Int,String](2 -> "Two")
y: scala.collection.immutable.Map[Int,String] = Map((1,One), (2,Two))
val z = x + (3 -> "Three")
z: scala.collection.immutable.HashMap[Int,String] = Map((1,One), (3,Three))

How to convert a mutable HashMap into an immutable equivalent in Scala?

Inside a function of mine I construct a result set by filling a new mutable HashMap with data (if there is a better way - I'd appreciate comments). Then I'd like to return the result set as an immutable HashMap. How to derive an immutable from a mutable?
Discussion about returning immutable.Map vs. immutable.HashMap notwithstanding, what about simply using the toMap method:
scala> val m = collection.mutable.HashMap(1 -> 2, 3 -> 4)
m: scala.collection.mutable.HashMap[Int,Int] = Map(3 -> 4, 1 -> 2)
scala> m.toMap
res22: scala.collection.immutable.Map[Int,Int] = Map(3 -> 4, 1 -> 2)
As of 2.9, this uses the method toMap in TraversableOnce, which is implemented as follows:
def toMap[T, U](implicit ev: A <:< (T, U)): immutable.Map[T, U] = {
val b = immutable.Map.newBuilder[T, U]
for (x <- self)
b += x
b.result
}
scala> val m = collection.mutable.HashMap(1->2,3->4)
m: scala.collection.mutable.HashMap[Int,Int] = Map(3 -> 4, 1 -> 2)
scala> collection.immutable.HashMap() ++ m
res1: scala.collection.immutable.Map[Int,Int] = Map(1 -> 2, 3 -> 4)
or
scala> collection.immutable.HashMap(m.toSeq:_*)
res2: scala.collection.immutable.HashMap[Int,Int] = Map(1 -> 2, 3 -> 4)
If you have a map : logMap: Map[String, String]
just need to do : logMap.toMap()