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))
Related
I have Map[String,Seq[String]].
I want to find the unique elements among all the values in the map. I want to do this in Scala.
Say, I have
Map['a' -> Seq(1,2,3),
'b' -> Seq(2,3),
'c' -> Seq(4)
]
I want the desired result to be
Map['a' -> Seq(3), 'c' -> Seq(4)]
Any idea on how to do this?
Thanks!
If you are looking for unique element in each list, then you can use currentList.diff(rest_of_the_list)
Given
scala> val input = Map('a' -> Seq(1,2,3), 'b' -> Seq(2,3), 'c' -> Seq(4))
input: scala.collection.immutable.Map[Char,Seq[Int]] = Map(a -> List(1, 2, 3), b -> List(2, 3), c -> List(4))
Find the rest of the elements for each key,
scala> val unions = input.map(elem => elem._1 -> input.filter(!_._1.equals(elem._1)).flatMap(_._2).toSet)
unions: scala.collection.immutable.Map[Char,scala.collection.immutable.Set[Int]] = Map(a -> Set(2, 3, 4), b -> Set(1, 2, 3, 4), c -> Set(1, 2, 3))
Then, iterate over input map and find the unique element in each each list
scala> input.map(x => x._1 -> x._2.diff(unions(x._1).toList))
res18: scala.collection.immutable.Map[Char,Seq[Int]] = Map(a -> List(1), b -> List(), c -> List(4))
If you don't want empty keys (b in above example)
scala> input.map(x => x._1 -> x._2.diff(unions(x._1).toList)).filter(_._2.nonEmpty)
res21: scala.collection.immutable.Map[Char,Seq[Int]] = Map(a -> List(1), c -> List(4))
Find the elements that non-unique by flattening all values and filter elements that size more than 1. Then, remove all non-unique element in every key.
val input = Map('a' -> Seq(1,2,3),
'b' -> Seq(2,3),
'c' -> Seq(4))
val nonUnique = input.values.flatten
.groupBy(identity)
.filter(_._2.size > 1)
.keys.toSeq
input.mapValues(x => x.diff(nonUnique)).filter(_._2.size == 1)
Suppose I have a Map like
val x = Map(1 -> List("a", "b"), 2 -> List("a"),
3 -> List("a", "b"), 4 -> List("a"),
5 -> List("c"))
How would I create from this a new Map where the keys are Lists of keys from x having the same value, e.g., how can I implement
def someFunction(m: Map[Int, List[String]]): Map[List[Int], List[String]] =
// stuff that would turn x into
// Map(List(1, 3) -> List("a", "b"), List(2, 4) -> List("a"), List(5) -> List("c"))
?
You can convert the Map to a List and then use groupBy to aggregate the first element of each tuple:
x.toList.groupBy(_._2).mapValues(_.map(_._1)).map{ case (x, y) => (y, x) }
// res37: scala.collection.immutable.Map[List[Int],List[String]] =
// Map(List(2, 4) -> List(a), List(1, 3) -> List(a, b), List(5) -> List(c))
Or as #Dylan commented, use _.swap to switch the tuples' elements:
x.toList.groupBy(_._2).mapValues(_.map(_._1)).map(_.swap)
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)
This question already has answers here:
Count occurrences of each element in a List[List[T]] in Scala
(3 answers)
Closed 9 years ago.
I'm new to Scala.
If I have the following List:
val ls = List("a", "a", "a", "b", "b", "c")
how can I create a Map that holds an number of appearances for every element in the list?
For example the Map for the list above should be:
Map("a" -> 3, "b" -> 2, "c" -> 1)
list.foldLeft(Map[String, Int]() withDefaultValue 0) { (m, x) => m + (x -> (m(x) + 1)) }
snippet in action:
scala> val list = List("a", "a", "b", "c", "c", "a")
list: List[String] = List(a, a, b, c, c, a)
scala> list.foldLeft(Map[String, Int]() withDefaultValue 0) { (m, x) => m + (x -> (1 + m(x))) }
res1: scala.collection.immutable.Map[String,Int] = Map(a -> 3, b -> 1, c -> 2)
(directly based on Count occurrences of each element in a List[List[T]] in Scala)
Not as efficient as Erik's foldLeft solution:
val ls = List("a", "a", "a", "b", "b", "c")
ls.groupBy(identity).mapValues(_.size)
res0: scala.collection.immutable.Map[String,Int] = Map(a -> 3, c -> 1, b -> 2)
With scalaz,
xs foldMap (x => Map(x -> 1))