Scala - Drop all mappings whose keys are not in a given set - scala

What is the idiomatic way to compute a map whose mappings are the mappings of another map, without the mappings whose keys are members of a given set?
For example, from the given set and map below:
Map(A -> v, B -> w, C -> x, D -> y, E -> z)
Set(A, C, E)
our function would yield:
Map(B -> w, D -> y)

You can use the -- method from Map
def --(xs: GenTraversableOnce[A]): Map[A, B]
Creates a new collection from this collection by removing all elements
of another collection.
scala> val map = Map("A" -> "v", "B" -> "w", "C" -> "x", "D" -> "y", "E" -> "z")
map: scala.collection.immutable.Map[String,String] = Map(E -> z, A -> v, B -> w, C -> x, D -> y)
scala> val set = Set("A", "C", "E")
set: scala.collection.immutable.Set[String] = Set(A, C, E)
scala> val filter = map -- set
filter: scala.collection.immutable.Map[String,String] = Map(B -> w, D -> y)

Just filter it:
val map = Map("A" -> "v", "B" -> "w", "C" -> "x", "D" -> "y", "E" -> "z")
val set = Set("A", "C", "E")
map.filterKeys(!set.contains(_))
Results in:
res0: scala.collection.immutable.Map[String,String] = Map(B -> w, D -> y)

Related

Invert a Map (String -> List) in Scala

I have a Map[String, List[String]] and I want to invert it. For example, if I have something like
"1" -> List("a","b","c")
"2" -> List("a","j","k")
"3" -> List("a","c")
The result should be
"a" -> List("1","2","3")
"b" -> List("1")
"c" -> List("1","3")
"j" -> List("2")
"k" -> List("2")
I've tried this:
m.map(_.swap)
But it returns a Map[List[String], String]:
List("a","b","c") -> "1"
List("a","j","k") -> "2"
List("a","c") -> "3"
Map inversion is a little more complicated.
val m = Map("1" -> List("a","b","c")
,"2" -> List("a","j","k")
,"3" -> List("a","c"))
m flatten {case(k, vs) => vs.map((_, k))} groupBy (_._1) mapValues {_.map(_._2)}
//res0: Map[String,Iterable[String]] = Map(j -> List(2), a -> List(1, 2, 3), b -> List(1), c -> List(1, 3), k -> List(2))
Flatten the Map into a collection of tuples. groupBy will create a new Map with the old values as the new keys. Then un-tuple the values by removing the key (previously value) elements.
An alternative that does not rely on strange implicit arguments of flatten, as requested by yishaiz:
val m = Map(
"1" -> List("a","b","c"),
"2" -> List("a","j","k"),
"3" -> List("a","c"),
)
val res = (for ((digit, chars) <- m.toList; c <- chars) yield (c, digit))
.groupBy(_._1) // group by characters
.mapValues(_.unzip._2) // drop redundant digits from lists
res foreach println
gives:
(j,List(2))
(a,List(1, 2, 3))
(b,List(1))
(c,List(1, 3))
(k,List(2))
A simple nested for-comprehension may be used to invert the map in such a way that each value in the List of values are keys in the inverted map with respective keys as their values
implicit class MapInverter[T] (map: Map[T, List[T]]) {
def invert: Map[T, T] = {
val result = collection.mutable.Map.empty[T, T]
for ((key, values) <- map) {
for (v <- values) {
result += (v -> key)
}
}
result.toMap
}
Usage:
Map(10 -> List(3, 2), 20 -> List(16, 17, 18, 19)).invert

Scala two map merge

How can I merge maps like below:
val map1 = Map(1 -> "a", 2 -> "b")
val map2 = Map("a" -> "A", "b" -> "B")
After merged.
Merged = Map( 1 -> List("a", "A"), 2 -> List("b", "B"))
Can be List, Set or any other collection who has size attribute.
I'm not sure I understand what are you searching for exactly, but to achieve that for the provided example you could do:
val map1 = Map(1 -> "a", 2 -> "b")
val map2 = Map("a" -> "A", "b" -> "B")
map1.mapValues(value => (value, map2(value)))
However you should be careful to have every value from a as a key in b (I just assumed this happens from the provided example).
Given two maps with value1 as key2
scala> val x = Map(1 -> "a", 2 -> "b")
x: scala.collection.immutable.Map[Int,String] = Map(1 -> a, 2 -> b)
scala> val y = Map("a" -> "A", "b" -> "B")
y: scala.collection.immutable.Map[String,String] = Map(a -> A, b -> B)
Merge as Map(k1 -> List(v1, v2))
scala> val z = x.map { case (k1, v1) => (k1, List(v1, y(v1))) }
z: scala.collection.immutable.Map[Int,List[String]] = Map(1 -> List(a, A), 2 -> List(b, B))
You basically need to get value from first map then lookup the second map, and just create a List out of those (v1, v2).
Try This
scala> val map1 = Map(1 -> "a", 2 -> "b")
map1: scala.collection.immutable.Map[Int,String] = Map(1 -> a, 2 -> b)
scala> val map2 = Map("a" -> "A", "b" -> "B")
map2: scala.collection.immutable.Map[String,String] = Map(a -> A, b -> B)
scala> map1.zip(map2).map(x=>x._1._1 -> List(x._2._1,x._2._2))
res44: scala.collection.immutable.Map[Int,List[String]] = Map(1 -> List(a, A), 2 -> List(b, B))

Scala - Undo a flatmap after transformation

How can I merge a Seq of Maps to a single Map i.e.
Seq[Map[String, String]] => Map[String, String]
For example:
val someSeq = rdd.map(_._2).flatMap(...) //some transformation to produce the sequence of maps
where someSeq is Seq(student1, student2) and student1 and student2 are Maps :
var student1 = Map(a -> "1", b -> "1")
var student2 = Map(c -> "1", d -> "1")
I need a result like this:
val apps = Map(a -> "1", b -> "1", c -> "1", d -> "1")
Any idea ?
Unrelated to Spark, but one approach would be to fold over the sequence as follows:
val student1 = Map("a" -> "1", "b" -> "1")
val student2 = Map("c" -> "1", "d" -> "1")
val students = Seq(student1, student2)
students.foldLeft(Map[String, String]())(_ ++ _)
Returns
Map(a -> 1, b -> 1, c -> 1, d -> 1)
In regards to "undoing" a flatMap, I don't believe this is really possible. In order to achieve that, consider the notion of undoing a "flatten".
For example:
val x = Seq(1, 2)
val y = Seq(3, 4)
val combined = Seq(x, y)
val flattened = combined.flatten
val b = Seq(1, 2, 3)
val c = Seq(4)
val combined2 = Seq(b, c)
val flattened2 = combined2.flatten
flattened == flattened2
Returns true.
So basically, in this instance, you can go from unflattened to flattened, but not vice versa, because vice versa would yield multiple answers.

Scala troubles with sorting

I am still on studying period when it comes to scala and faces some problems that I would like to solve.
What I have at the moment is a Seq of items type X. Now I want to make a function that returns me a map of numbers mapped with set of items that appear on that original seq certain amount of time.
Here is small example what I want to do:
val exampleSeq[X]: Seq = [a, b, d, d, c, b, d]
val exampleSeq2[x]: Seq = [a, a, a, c, c, b, b, c]
myMagicalFunction(exampleSeq) returns Map[1 -> Set[a, c], 2 -> Set[b], 3 -> Set[d]]
myMagicalFunction(exampleSeq2) returns Map[2 -> Set[b], 3 -> Set[a, c]]
So far I have been able to create a function that maps the item with the times it appears:
function[X](seq: Seq[X]) = seq.groupBy(item => item).mapValues(_.size)
Return for my exampleSeq from that one is
Map(a -> 1, b -> 2, c -> 1, d -> 3)
Thank you for answers :)
One approach, for
val a = Seq('a', 'b', 'd', 'd', 'c', 'b', 'd')
this
val b = for ( (k,v) <- a.groupBy(identity).mapValues(_.size).toArray )
yield (v,k)
delivers
Array((2,b), (3,d), (1,a), (1,c))
and so
b.groupBy(_._1).mapValues(_.map(_._2).toSet)
res: Map(2 -> Set(b), 1 -> Set(a, c), 3 -> Set(d))
Note seq.groupBy(item => item) is equivalent to seq.groupBy(identity).
You are almost there! Departing from the collection element -> count, you only need a transformation to get to count -> Col[elem].
Lets say that freqItem = Map(a -> 1, b -> 2, c -> 1, d -> 3) you would do something like:
val freqSet = freqItem.toSeq.map(_.swap).groupBy(_._1).mapValues(_.toSet)
Note that we transform the Map into a Seq before swapping the (k,v) into (v,k) because mapping over a Map preserves the semantics of key uniqueness and you'd lose one of (1 -> a), (1 -> b) otherwise.
You can write your function as :
def f[T](l: Seq[T]): Map[Int, Set[T]] = {
l.map {
x => (x, l.count(_ == x))
}.distinct.groupBy(_._2).mapValues(_.map(_._1).toSet)
}
val l = List("a","a","a","b","b","b","b","c","c","d","e")
f(l)
res0: Map[Int,Set[String]] = Map(2 -> Set(c), 4 -> Set(b), 1 -> Set(d, e), 3 -> Set(a))
scala> case class A(name:String,age:Int)
defined class A
scala> val l = List(new A("a",1),new A("b",2),new A("a",1),new A("c",1) )
l: List[A] = List(A(a,1), A(b,2), A(a,1), A(c,1))
scala> f[A](l)
res1: Map[Int,Set[A]] = Map(2 -> Set(A(a,1)), 1 -> Set(A(b,2), A(c,1)))

Recursively walk values in Map

I am attempting to walk a Map[String,List[String]] recursively to extract and flatten all values associated with a Map
val x = Map("a" -> List("b","c","d"), "b" -> List("f","g","h"), "f" -> List("i","j","k"), "g" -> List("p","q","r"))
For each of the keys, extract values i.e. List
For each item in values of List:
Check if key exists and then extract values
Continue to do so recursively till the keys have no values and flatten values of list for key
The result should be
Map("a" -> List("b","c","d","f","g","h","i","j","k","p","q","r"),
"b" -> List("f","g","h","i","j","k","p","q","r"),
"f" -> List("i","j","k"),
"g" -> List("p","q","r"))
You can try to iterate until there is no change:
def getValues(dict: Map[String, List[String]]) = Iterator.iterate(dict) { _.mapValues {
_.flatMap(v => v :: dict.get(v).toList.flatten).toSet.toList
} filterNot { _._2.isEmpty }
}.sliding(2) find { x => x.head == x.last }
This definitely is not the most efficient solution, but it is pretty terse!
Try this code:
def f(map: Map[String, List[String]]): Map[String, List[String]] = {
def f(x: Map[String, List[String]], acc: Map[String, List[String]]): Map[String, List[String]] = {
if (x.isEmpty) acc
else {
val keys = x.keySet
val (complex, simple) = x partition {_._2 exists {s => keys contains s}}
val newX =
(for ((ck, cl) <- complex)
yield (ck -> (simple.filter(x => cl.contains (x._1)).map(_._2).flatten ++ cl).toList)).toMap
f(newX, acc ++ simple)
}
}
f(map, Map.empty)
}
val x = Map("a" -> List("b","c","d"), "b" -> List("f", "g", "h"), "f" -> List("i","j","k"), "g" -> List("p","q","r"))
println(f(x)) //Map(f -> List(i, j, k), g -> List(p, q, r), b -> List(i, j, k, p, q, r, f, g, h), a -> List(i, j, k, p, q, r, f, g, h, b, c, d))
However it is assumed that there is no recursion in the map e.g. ("a" -> List("b")), ("b" -> List("a"). If it happens the function will end up in infinite loop. You would have to add extra code to handle such situations.