To make it simple, let's imagine I have the following input:
List(List("A", "A"), List("A", "B"), List("B", "C"), List("B", "C"))
How would it be possible to group the elements inside the lists in such fashion so that I would know how many lists are they in. For example, following the output of a mapValues function just to illustrate what I mean, the result of the previous input should be something like:
Map("A" -> 2, "B" -> 3, "C" -> 2)
Just to be sure I made clear what I mean, a way to interpret the result would be to say that "A" is present in 2 of the sub-lists (regardless of how many times it appears inside of a particular sub-list), "B" is present in 3 of the sub-lists and "C" is in 2. I just want a way to map how many different sub-lists each of the individual elements are present in.
Disregarding performance, this would work:
val list = List(List("A", "A"), List("A", "B"), List("B", "C"), List("B", "C"))
val elements = list.flatten.distinct
elements.map(el => el -> list.count(_.contains(el))).toMap
You can also use fold operation to like this
list.flatten.foldLeft(Map.empty[String, Int])((map, word) => map + (word -> (map.getOrElse(word,0) + 1)))
//scala> res2: scala.collection.immutable.Map[String,Int] = Map(A -> 3, B -> 3, C -> 2)
Related
Is there a function in scala that groups all elements of a list by the number of these occurrences?
For example, I have this list:
val x = List("c", "b", "b", "c", "a", "d", "c")
And I want to get a new list like that:
x = List((3, "c"), (2, "b"), (1, "a"), (1, "d"))
You can first count the occurrences of each element and then reverse the resulting tuples:
List("c", "b", "b", "c", "a", "d", "c")
.groupBy(identity).mapValues(_.size) // Map(b -> 2, d -> 1, a -> 1, c -> 3)
.toList // List((b,2), (d,1), (a,1), (c,3))
.map{ case (k, v) => (v, k) } // List((2,b), (1,d), (1,a), (3,c))
You don't specifically mention a notion of order for the output, but if this was a requirement, this solution would need to be adapted.
Try this to get exactly what you want in the order you mentioned. (ie., order preserved in the List while taking counts):
x.distinct.map(v=>(x.filter(_==v).size,v))
In SCALA REPL:
scala> val x = List("c", "b", "b", "c", "a", "d", "c")
x: List[String] = List(c, b, b, c, a, d, c)
scala> x.distinct.map(v=>(x.filter(_==v).size,v))
res225: List[(Int, String)] = List((3,c), (2,b), (1,a), (1,d))
scala>
I am scratching my head vigorously, to understand the logic that produces the value out of a flatMap() operation:
val ys = Map("a" -> List(1 -> 11,1 -> 111), "b" -> List(2 -> 22,2 -> 222)).flatMap(e => {
| println("e =" + e)
| (e._2)
| })
e =(a,List((1,11), (1,111)))
e =(b,List((2,22), (2,222)))
ys: scala.collection.immutable.Map[Int,Int] = Map(1 -> 111, 2 -> 222)
The println clearly shows that flatMap is taking in one entry out of the input Map. So, e._2 is a List of Pairs. I can't figure out what exactly happens after that!
I am missing a very important and subtle step somewhere. Please enlighten me.
It can be thought of as:
First we map:
val a = Map("a" -> List(1 -> 11,1 -> 111), "b" -> List(2 -> 22,2 -> 222)).map(e => e._2)
// List(List((1, 11), (1, 111)), List((2, 22), (2, 222)))
Then we flatten:
val b = a.flatten
// List((1, 11), (1, 111), (2, 22), (2, 222))
Then we convert back to a map:
b.toMap
// Map(1 -> 111, 2 -> 222)
Since a map cannot have 2 values for 1 key, the value is overwritten.
Really whats going on is that the flatMap is being converted into a loop like so:
for (x <- m0) b ++= f(x)
where:
m0 is our original map
b is a collection builder that has to build a Map, aka, MapBuilder
f is our function being passed into the flatMap (it returns a List[(Int, Int)])
x is an element in our original map
The ++= function takes the list we got from calling f(x), and calls += on every element, to add it to our map. For a Map, += just calls the original + operator for a Map, which updates the value if the key already exists.
Finally we call result on our builder which just returns us our Map.
I have two lists
val a = List(1,2,3)
val b = List(5,6,7)
I'd like to create a Map like:
val h = Map(1->5, 2->6, 3->7)
basically iterating thru both the lists and assigning key value pairs.
How to do it properly in Scala?
You can zip the lists together into a list of tuples, then call toMap:
(a zip b) toMap
Note that if one list is longer than the other, it will be truncated.
Example:
val a = List(1, 2, 3)
val b = List(5, 6, 7)
scala> (a zip b) toMap
res2: scala.collection.immutable.Map[Int,Int] = Map(1 -> 5, 2 -> 6, 3 -> 7)
With truncation:
val c = List("a", "b", "c", "d", "e")
scala> (a zip c) toMap
res3: scala.collection.immutable.Map[Int,String] = Map(1 -> a, 2 -> b, 3 -> c)
(c zip a) toMap
res4: scala.collection.immutable.Map[String,Int] = Map(a -> 1, b -> 2, c -> 3)
I have an array, something like that:
val a = Array("a", "c", "c", "z", "c", "b", "a")
and I want to get a map with keys of all different values of this array and values with a collection of relevant indexes for each such group, i.e. for a given array the answer would be:
Map(
"a" -> Array(0, 6),
"b" -> Array(5),
"c" -> Array(1, 2, 4),
"z" -> Array(3)
)
Surprisingly, it proved to be somewhat more complicated that I've anticipated. The best I've came so far with is:
a.zipWithIndex.groupBy {
case(cnt, idx) => cnt
}.map {
case(cnt, arr) => (cnt, arr.map {
case(k, v) => v
}
}
which is not either concise or easy to understand. Any better ideas?
Your code can be rewritten as oneliner, but it looks ugly.
as.zipWithIndex.groupBy(_._1).mapValues(_.map(_._2))
Another way is to use mutable.MultiMap
import collection.mutable.{ HashMap, MultiMap, Set }
val as = Array("a", "c", "c", "z", "c", "b", "a")
val mm = new HashMap[String, Set[Int]] with MultiMap[String, Int]
and then just add every binding
as.zipWithIndex foreach (mm.addBinding _).tupled
//mm = Map(z -> Set(3), b -> Set(5), a -> Set(0, 6), c -> Set(1, 2, 4))
finally you can convert it mm.toMap if you want immutable version.
Here's a version with foldRight. I think it's reasonably clear.
val a = Array("a", "c", "c", "z", "c", "b", "a")
a
.zipWithIndex
.foldRight(Map[String, List[Int]]())
{case ((e,i), m)=> m updated (e, i::m.getOrElse(e, Nil))}
//> res0: scala.collection.immutable.Map[String,List[Int]] = Map(a -> List(0, 6)
//| , b -> List(5), c -> List(1, 2, 4), z -> List(3))
Another version using foldLeft and an immutable Map with default value:
val a = Array("a", "c", "c", "z", "c", "b", "a")
a.zipWithIndex.foldLeft(Map[String, List[Int]]().withDefaultValue(Nil))( (m, p) => m + ((p._1, p._2 +: m(p._1))))
// res6: scala.collection.immutable.Map[String,List[Int]] = Map(a -> List(6, 0), c -> List(4, 2, 1), z -> List(3), b -> List(5))
Starting in Scala 2.13, we can use the new groupMap which (as its name suggests) is a one-pass equivalent of a groupBy and a mapping over grouped items:
// val a = Array("a", "c", "c", "z", "c", "b", "a")
a.zipWithIndex.groupMap(_._1)(_._2)
// Map("z" -> Array(3), "b" -> Array(5), "a" -> Array(0, 6), "c" -> Array(1, 2, 4))
This:
zips each item with its index, giving (item, index) tuples
groups elements based on their first tuple part (_._1) (group part of groupMap)
maps grouped values to their second tuple part (_._2 i.e. their index) (map part of groupMap)
I have a list of pairs:
val pairs = List("a" -> 1, "b" -> 2, "c" -> 3)
I'd like to convert it to a pair of lists:
List("a", "b", "c") -> List(1, 2, 3)
Basically, I want the opposite of zip()
Any elegant way of doing so?
The opposite of zip? What might that be? unzip maybe?
scala> List("a" -> 1, "b" -> 2, "c" -> 3).unzip
res0: (List[java.lang.String], List[Int]) = (List(a, b, c),List(1, 2, 3))