How to convert two consecutive elements from List to entries in Map? - scala

I have a list:
List(1,2,3,4,5,6)
that I would like to to convert to the following map:
Map(1->2,3->4,5->6)
How can this be done?

Mostly resembles #Vakh answer, but with a nicer syntax:
val l = List(1,2,3,4,5,6)
val m = l.grouped(2).map { case List(key, value) => key -> value}.toMap
// Map(1 -> 2, 3 -> 4, 5 -> 6)

Try:
val l = List(1,2,3,4,5,6)
val m = l.grouped(2).map(l => (l(0), l(1))).toMap

if the list is guaranteed to be of even length:
val l = List(1,2,3,4,5,6)
val m = l.grouped(2).map { x => x.head -> x.tail.head }.toMap
// Map(1 -> 2, 3 -> 4, 5 -> 6)
but if list may be of odd length, use headOption:
val l = List(1,2,3,4,5,6,7)
val m = l.grouped(2).map(x => x.head -> x.tail.headOption).toMap
// Map(1 -> Some(2), 3 -> Some(4), 5 -> Some(6), 7 -> None)

Without using grouped that appears ubiquitous in the answers so far.
scala> val l = (1 to 6).toList
l: List[Int] = List(1, 2, 3, 4, 5, 6)
scala> l.zip(l.tail).zipWithIndex.collect { case (e, pos) if pos % 2 == 0 => e }.toMap
res0: scala.collection.immutable.Map[Int,Int] = Map(1 -> 2, 3 -> 4, 5 -> 6)
You may also use sliding and foldLeft as follows:
scala> l.sliding(2,2).foldLeft(Map.empty[Int,Int]){ case (m, List(l, r)) => m + (l -> r) }
res1: scala.collection.immutable.Map[Int,Int] = Map(1 -> 2, 3 -> 4, 5 -> 6)

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)

Removing elements from a map of type (Int, ListBuffer(Int))

I have LinkedHashMaps of type:
val map1 = LinkedHashMap(1 -> 1, 2 -> (1,2), 3 -> (1,2,3))
val map2 = LinkedHashMap(2 -> 2, 3 -> (2,3), 5 -> (2,3,5))
where the integers are nodes's ids of a graph, and the list is the path to that node. I want to implement the case of deleting a node. Suppose I want to delete node 3, I have to do two actions: remove the element with key = 3 in every map, remove the elements which have 3 in their list. How to do it in scala?
If you define you map like you have,
val map1 = LinkedHashMap(1 -> 1, 2 -> (1,2), 3 -> (1,2,3))
You do not have key: Int and value: List[Int] but you have key: Int and values: Any.
scala> val map1 = LinkedHashMap(1 -> 1, 2 -> (1,2), 3 -> (1,2,3))
// map1: scala.collection.mutable.LinkedHashMap[Int,Any] = Map(1 -> 1, 2 -> (1,2), 3 -> (1,2,3))
To match your requirement, you should define your map like following,
scala> val map1 = LinkedHashMap(1 -> List(1), 2 -> List(1,2), 3 -> List(1,2,3))
// map1: scala.collection.mutable.LinkedHashMap[Int,List[Int]] = Map(1 -> List(1), 2 -> List(1, 2), 3 -> List(1, 2, 3))
Now, if you want to delete a node 3,
scala> val map2 = map1.filter({
| case (key, list) => key != 3 && !list.contains(3)
| })
// map2: scala.collection.mutable.LinkedHashMap[Int,List[Int]] = Map(1 -> List(1), 2 -> List(1, 2))

How do I do a Map comprehension with Scala?

With Python, I can do something like
listOfLists = [('a', -1), ('b', 0), ('c', 1)]
my_dict = {foo: bar for foo, bar in listOfLists}
my_dict == {'a': -1, 'b': 0, 'c': 1} => True
I know this as a dictionary comprehension. When I look for this operation with Scala, I find this incomprehensible document (pun intended).
Is there an idiomatic way to do this with Scala?
Bonus question: Can I filter with this operation as well like my_dict = {foo: bar for foo, bar in listOfLists if bar > 0}?
First, let's parse your Python code to figure out what it's doing.
my_dict = {
foo: bar <-- Key, value names
for foo, bar <-- Destructuring a list
in listOfLists <-- This is where they came from
}
So you can see that even in this very short example there's actually considerable redundancy and plenty of potential for failure if listOfLists isn't actually what it says it is.
If listOfLists actually is a list of pairs (key, value), then in Scala it's trivial:
listOfPairs.toMap
If, on the other hand, it really is lists, and you want to pull off the first one to make the key and save the rest as a value, it would be something like
listOfLists.map(x => x.head -> x.tail).toMap
You can select some of them by using collect instead. For instance, maybe you only want the lists of length 2 (you could if x.head > 0 to get your example), in which case you
listOfLists.collect{
case x if x.length == 2 => x.head -> x.last
}.toMap
or if it is literally a List, you could also
listOfLists.collect{
case key :: value :: Nil => key -> value
}.toMap
I'll compare list comprehension in Scala2.x and Python 3.x
1. Sequence
In python:
xs = [x*x for x in range(5)]
#xs = [0, 1, 4, 9, 16]
ys = list(map(lambda x: x*x, range(5)))
#ys = [0, 1, 4, 9, 16]
In Scala:
scala> val xs = for(x <- 0 until 5) yield x*x
xs: scala.collection.immutable.IndexedSeq[Int] = Vector(0, 1, 4, 9, 16)
scala> val ys = (0 until 5) map (x => x*x)
ys: scala.collection.immutable.IndexedSeq[Int] = Vector(0, 1, 4, 9, 16)
Or you really want a list:
scala> import collection.breakOut
scala> val xs: List[Int] = (for(x <- 0 until 5) yield x*x)(breakOut)
xs: List[Int] = List(0, 1, 4, 9, 16)
scala> val ys: List[Int] = (0 until 5).map(x => x*x)(breakOut)
ys: List[Int] = List(0, 1, 4, 9, 16)
scala> val zs = (for(x <- 0 until 5) yield x*x).toList
zs: List[Int] = List(0, 1, 4, 9, 16)
2. Set
In Python
s1 = { x//2 for x in range(10) }
#s1 = {0, 1, 2, 3, 4}
s2 = set(map(lambda x: x//2, range(10)))
#s2 = {0, 1, 2, 3, 4}
In Scala
scala> val s1 = (for(x <- 0 until 10) yield x/2).toSet
s1: scala.collection.immutable.Set[Int] = Set(0, 1, 2, 3, 4)
scala> val s2: Set[Int] = (for(x <- 0 until 10) yield x/2)(breakOut)
s2: Set[Int] = Set(0, 1, 2, 3, 4)
scala> val s3: Set[Int] = (0 until 10).map(_/2)(breakOut)
s3: Set[Int] = Set(0, 1, 2, 3, 4)
scala> val s4 = (0 until 10).map(_/2).toSet
s4: scala.collection.immutable.Set[Int] = Set(0, 1, 2, 3, 4)
3. Dict
In Python:
pairs = [(1, 'a'), (2, 'b'), (3, 'c'), (4, 'd')]
#d1 = {1: 'aa', 2: 'bb', 3: 'cc', 4: 'dd'}
d2 = dict([(k*2, v) for k, v in pairs])
#d2 = {2: 'a', 4: 'b', 6: 'c', 8: 'd'}
In Scala
scala> val pairs = Seq(1->"a", 2->"b", 3->"c", 4->"d")
pairs: Seq[(Int, String)] = List((1,a), (2,b), (3,c), (4,d))
scala> val d1 = (for((k, v) <- pairs) yield (k, v*2)).toMap
d1: scala.collection.immutable.Map[Int,String] = Map(1 -> aa, 2 -> bb, 3 -> cc, 4 -> dd)
scala> val d2 = Map(pairs map { case(k, v) => (k*2, v) } :_*)
d2: scala.collection.immutable.Map[Int,String] = Map(2 -> a, 4 -> b, 6 -> c, 8 -> d)
scala> val d3 = pairs map { case(k, v) => (k*2, v) } toMap
d3: scala.collection.immutable.Map[Int,String] = Map(2 -> a, 4 -> b, 6 -> c, 8 -> d)
scala> val d4: Map[Int, String] = (for((k, v) <- pairs) yield (k, v*2))(breakOut)
d4: Map[Int,String] = Map(1 -> aa, 2 -> bb, 3 -> cc, 4 -> dd)
Here are a few examples:
val listOfLists = Vector(Vector(1,2), Vector(3,4), Vector(5,6))
val m1 = listOfLists.map { case Seq(a,b) => (a,b) }.toMap
val m2 = listOfLists.collect { case Seq(a,b) if b>0 => (a,b) }.toMap
val m3 = (for (Seq(a,b) <- listOfLists) yield (a,b)).toMap
val m4 = (for (Seq(a,b) <- listOfLists if b>0) yield (a,b)).toMap
val m5 = Map(listOfLists.map { case Seq(a,b) => (a,b) }: _*)
val m6 = Map(listOfLists.collect { case Seq(a,b) => (a,b) }: _*)
val m7 = Map((for (Seq(a,b) <- listOfLists) yield (a,b)): _*)
val m8 = Map((for (Seq(a,b) <- listOfLists if b>0) yield (a,b)): _*)
You can create a Map using .toMap or Map(xs: _*). The collect method lets you filter as you map. And a for-comprehension uses syntax most similar to your example.

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)

Remove an entry from a Map and return a new Map

I want to check if a Map doesn't contain empty values. If the value is empty it shouldn't includen in the new Map.
I tried something like:
val newmap = map.map{ entry => if(!entry._2.isEmpty()) Map(entry._1 -> entry._2)}
This does exactly do what I want, but it is not very nice. Is there a better solution?
scala> Map(1 -> List(3, 4), 2 -> Nil, 3 -> List(11))
res2: scala.collection.immutable.Map[Int,List[Int]] = Map(1 -> List(3, 4), 2 -> List(), 3 -> List(11))
scala> res2.filter(_._2.nonEmpty)
res3: scala.collection.immutable.Map[Int,List[Int]] = Map(1 -> List(3, 4), 3 -> List(11))
scala>
You mean empty as in null?
scala> val map = collection.immutable.HashMap[Int, String] (1 -> "a", 2-> "b", 3 -> null)
map: scala.collection.immutable.HashMap[Int,String] = Map(1 -> a, 2 -> b, 3 -> null)
scala> val newmap=map filter (_._2 != null)
newmap: scala.collection.immutable.HashMap[Int,String] = Map(1 -> a, 2 -> b)
EDIT: dang... #missingfaktor beat me to it... :)