What is the most succinct Scala way to reverse a Map? - scala

What is the most succinct Scala way to reverse a Map? The Map may contain non-unique values.
EDIT:
The reversal of Map[A, B] should give Map[B, Set[A]] (or a MultiMap, that would be even better).

If you can lose duplicate keys:
scala> val map = Map(1->"one", 2->"two", -2->"two")
map: scala.collection.immutable.Map[Int,java.lang.String] = Map((1,one), (2,two), (-2,two))
scala> map.map(_ swap)
res0: scala.collection.immutable.Map[java.lang.String,Int] = Map((one,1), (two,-2))
If you don't want access as a multimap, just a map to sets, then:
scala> map.groupBy(_._2).mapValues(_.keys.toSet)
res1: scala.collection.immutable.Map[
java.lang.String,scala.collection.immutable.Set[Int]
] = Map((one,Set(1)), (two,Set(2, -2)))
If you insist on getting a MultiMap, then:
scala> import scala.collection.mutable.{HashMap, Set, MultiMap}
scala> ( (new HashMap[String,Set[Int]] with MultiMap[String,Int]) ++=
| map.groupBy(_._2).mapValues(Set[Int]() ++= _.keys) )
res2: scala.collection.mutable.HashMap[String,scala.collection.mutable.Set[Int]]
with scala.collection.mutable.MultiMap[String,Int] = Map((one,Set(1)), (two,Set(-2, 2)))

scala> val m1 = Map(1 -> "one", 2 -> "two", 3 -> "three", 4 -> "four")
m1: scala.collection.immutable.Map[Int,java.lang.String] = Map((1,one), (2,two), (3,three), (4,four))
scala> m1.map(pair => pair._2 -> pair._1)
res0: scala.collection.immutable.Map[java.lang.String,Int] = Map((one,1), (two,2), (three,3), (four,4))
Edit for clarified question:
object RevMap {
def
main(args: Array[String]): Unit = {
val m1 = Map("one" -> 3, "two" -> 3, "three" -> 5, "four" -> 4, "five" -> 5, "six" -> 3)
val rm1 = (Map[Int, Set[String]]() /: m1) { (map: Map[Int, Set[String]], pair: (String, Int)) =>
map + ((pair._2, map.getOrElse(pair._2, Set[String]()) + pair._1)) }
printf("m1=%s%nrm1=%s%n", m1, rm1)
}
}
% scala RevMap
m1=Map(four -> 4, three -> 5, two -> 3, six -> 3, five -> 4, one -> 3)
rm1=Map(4 -> Set(four, five), 5 -> Set(three), 3 -> Set(two, six, one))
I'm not sure this qualifies as succinct.

How about:
implicit class RichMap[A, B](map: Map[A, Seq[B]])
{
import scala.collection.mutable._
def reverse: MultiMap[B, A] =
{
val result = new HashMap[B, Set[A]] with MultiMap[B, A]
map.foreach(kv => kv._2.foreach(result.addBinding(_, kv._1)))
result
}
}
or
implicit class RichMap[A, B](map: Map[A, Seq[B]])
{
import scala.collection.mutable._
def reverse: MultiMap[B, A] =
{
val result = new HashMap[B, Set[A]] with MultiMap[B, A]
map.foreach{case(k,v) => v.foreach(result.addBinding(_, k))}
result
}
}

Related

Scala-Cats: Validating Map's entries

Let's say I have:
val m: Map[String, Int] = Map("one" -> 1, "five" -> 5, "six" -> 6, "nine" -> 9)
and I have two functions:
def isNotDivisibleByTwo(i: Int): ValidatedNec[String, Int] = Validated.condNec(i%2!=0, i, s"$i is divisible by 2.")
def isNotDivisibleByThree(i: Int): ValidatedNec[String, Int] = Validated.condNec(i%3!=0, i, s"$i is divisible by 3.")
I want a function that gives me:
def sanitize(m: Map[String, Int]):Map[String, Validated[NonEmptyList[String], Int]] = ???
i.e. It should return all the numbers that satisfy the said two functions, and a mapping of all the failing numbers and their associated faults.
e.g. For the given list m, I want to get:
val result = Map(
"one" -> Valid(1),
"five -> Valid(5),
"nine" -> Invalid(NonEmptyList("9 is dividible by 3")),
"six" -> Invalid(NonEmptyList("6 is dividible by 2", "6 is dividible by 3"))
)
This is what I currently have:
import cats.data._
val m: Map[String, Int] = Map("one" -> 1, "five" -> 5, "six" -> 6, "nine" -> 9)
def isNotDivisibleByTwo(i: Int): ValidatedNec[String, Unit] = Validated.condNec(i%2!=0, (), s"$i is divisible by 2.")
def isNotDivisibleByThree(i: Int): ValidatedNec[String, Unit] = Validated.condNec(i%3!=0, (), s"$i is divisible by 3.")
def sanitize(m: Map[String, Int]): Map[String, Validated[NonEmptyChain[String], Int]] = {
m.mapValues{
i =>
isNotDivisibleByTwo(i).product(
isNotDivisibleByThree(i)
).map(_ => i)
}
}
But, I am not happy with the way I am "composing" the validations.
How can I do this in the most catsy way?
You were so close.
Remember that the correct way to combine multiple Validates is using the Applicative syntax.
import cats.data.{Validated, ValidatedNec}
import cats.syntax.apply._
type ErrorsOr[A] = ValidatedNec[String, A]
def isNotDivisibleByTwo(i: Int): ErrorsOr[Int] =
Validated.condNec((i % 2) != 0, i, s"$i is divisible by 2.")
def isNotDivisibleByThree(i: Int): ErrorsOr[Int] =
Validated.condNec((i % 3) != 0, i, s"$i is divisible by 3.")
val map: Map[String, Int] = Map("one" -> 1, "five" -> 5, "six" -> 6, "nine" -> 9)
def sanitize(m: Map[String, Int]): Map[String, ErrorsOr[Int]] =
m.view.mapValues { i =>
(
isNotDivisibleByTwo(i),
isNotDivisibleByThree(i)
).tupled.map(_ => i)
}.toMap
sanitize(map)
// res: Map[String, ErrorsOr[Int]] = Map(
// "one" -> Valid(1),
// "five" -> Valid(5),
// "six" -> Invalid(Append(Singleton("6 is divisible by 2."), Singleton("6 is divisible by 3."))),
// "nine" -> Invalid(Singleton("9 is divisible by 3."))
// )
However, you may make the code even more general, to work with any number of validations. By using traverse.
(In this case, you do not need any syntax import).
import cats.data.NonEmptyList
val validations: NonEmptyList[Int => ErrorsOr[Int]] = NonEmptyList.of(isNotDivisibleByTwo, isNotDivisibleByThree)
def sanitize[K, V](map: Map[K, V])
(validations: NonEmptyList[V => ErrorsOr[V]]): Map[K, ErrorsOr[V]] =
map.view.mapValues(i => validations.traverse(f => f(i)).map(_ => i)).toMap
sanitize(map)(validations)
// res: Map[String, ErrorsOr[Int]] = Map(
// "one" -> Valid(1),
// "five" -> Valid(5),
// "six" -> Invalid(Append(Singleton("6 is divisible by 2."), Singleton("6 is divisible by 3."))),
// "nine" -> Invalid(Singleton("9 is divisible by 3."))
// )
The reason why I use .view.mapValues(...).toMap is because on Scala 2.13 mapValues is deprecated.

Scala groupBy for a list

I'd like to create a map on which the key is the string and the value is the number of how many times the string appears on the list. I tried the groupBy method, but have been unsuccessful with that.
Required Answer
scala> val l = List("abc","abc","cbe","cab")
l: List[String] = List(abc, abc, cbe, cab)
scala> l.groupBy(identity).mapValues(_.size)
res91: scala.collection.immutable.Map[String,Int] = Map(cab -> 1, abc -> 2, cbe -> 1)
Suppose you have a list as
scala> val list = List("abc", "abc", "bc", "b", "abc")
list: List[String] = List(abc, abc, bc, b, abc)
You can write a function
scala> def generateMap(list: List[String], map:Map[String, Int]) : Map[String, Int] = list match {
| case x :: y => if(map.keySet.contains(x)) generateMap(y, map ++ Map(x -> (map(x)+1))) else generateMap(y, map ++ Map(x -> 1))
| case Nil => map
| }
generateMap: (list: List[String], map: Map[String,Int])Map[String,Int]
Then call the function as
scala> generateMap(list, Map.empty)
res1: Map[String,Int] = Map(abc -> 3, bc -> 1, b -> 1)
This also works:
scala> val l = List("abc","abc","cbe","cab")
val l: List[String] = List(abc, abc, cbe, cab)
scala> l.groupBy(identity).map(x => (x._1, x._2.length))
val res1: Map[String, Int] = HashMap(cbe -> 1, abc -> 2, cab -> 1)

How to tell if a Map has a default value?

Is there a way to check if a Map has a defined default value? What I would like is some equivalent of myMap.getOrElse(x, y) where if the key x is not in the map,
if myMap has a default value, return that value
else return y
A contrived example of the issue:
scala> def f(m: Map[String, String]) = m.getOrElse("hello", "world")
f: (m: Map[String,String])String
scala> val myMap = Map("a" -> "A").withDefaultValue("Z")
myMap: scala.collection.immutable.Map[String,String] = Map(a -> A)
scala> f(myMap)
res0: String = world
In this case, I want res0 to be "Z" instead of "world", because myMap was defined with that as a default value. But getOrElse doesn't work that way.
I could use m.apply instead of m.getOrElse, but the map is not guaranteed to have a default value, so it could throw an exception (I could catch the exception, but this is nonideal).
scala> def f(m: Map[String, String]) = try {
| m("hello")
| } catch {
| case e: java.util.NoSuchElementException => "world"
| }
f: (m: Map[String,String])String
scala> val myMap = Map("a" -> "A").withDefaultValue("Z")
myMap: scala.collection.immutable.Map[String,String] = Map(a -> A)
scala> f(myMap)
res0: String = Z
scala> val mapWithNoDefault = Map("a" -> "A")
mapWithNoDefault: scala.collection.immutable.Map[String,String] = Map(a -> A)
scala> f(mapWithNoDefault)
res1: String = world
The above yields the expected value but seems messy. I can't pattern match and call apply or getOrElse based on whether or not the map had a default value, because the type is the same (scala.collection.immutable.Map[String,String]) regardless of default-ness.
Is there a way to do this that doesn't involve catching exceptions?
You can check whether the map is an instance of Map.WithDefault:
implicit class EnrichedMap[K, V](m: Map[K, V]) {
def getOrDefaultOrElse(k: K, v: => V) =
if (m.isInstanceOf[Map.WithDefault[K, V]]) m(k) else m.getOrElse(k, v)
}
And then:
scala> val myMap = Map("a" -> "A").withDefaultValue("Z")
myMap: scala.collection.immutable.Map[String,String] = Map(a -> A)
scala> myMap.getOrDefaultOrElse("hello", "world")
res11: String = Z
scala> val myDefaultlessMap = Map("a" -> "A")
myDefaultlessMap: scala.collection.immutable.Map[String,String] = Map(a -> A)
scala> myDefaultlessMap.getOrDefaultOrElse("hello", "world")
res12: String = world
Whether this kind of reflection is any better than using exceptions for non-exceptional control flow is an open question.
You could use Try instead of try/catch, and it would look a little cleaner.
val m = Map(1 -> 2, 3 -> 4)
import scala.util.Try
Try(m(10)).getOrElse(0)
res0: Int = 0
val m = Map(1 -> 2, 3 -> 4).withDefaultValue(100)
Try(m(10)).getOrElse(0)
res1: Int = 100

SortedMap of SortedMaps from Map of List of objects, keyed on object member

I have an unordered map:
class O(val a: Int)
Map[String, List[O]]
which I'd like to turn into:
SortedMap[String, SortedMap[Int, O]]
with the child SortedMap keyed on the O field.
I'm sure there must be a more idiomatic code than the below...
class O(val a: Int)
val a: Map[String, List[O]] = Map[String, List[O]]( ("b" -> List(new O(3), new O(2))), "a" -> List(new O(1), new O(2)))
val key1s = a map (_._1)
val oMapsList = ListBuffer[SortedMap[Int, O]]()
for (key1 <- key1s) {
val oList = a(key1)
val key2s = oList map (_.a)
val sortedOMap = SortedMap[Int, O]() ++ (key2s zip oList).toMap
oMapsList += sortedOMap
}
val sortedMap = SortedMap[String, SortedMap[Int, O]]() ++ (key1s zip oMapsList).toMap
Expected sortedMap contents is:
"a" -> ( (1 -> O(1)),(2 -> O(2)) )
"b" -> ( (2 -> O(2)),(2 -> O(3)) )
Firstly, the setup:
scala> case class O(i: Int)
defined class O
scala> Map("a" -> List(O(1), O(2)), "b" -> List(O(2), O(3)))
res0: scala.collection.immutable.Map[java.lang.String,List[O]] = Map(a -> List(O(1), O(2)), b -> List(O(2), O(3)))
Now, import SortedMap:
scala> import collection.immutable._
import collection.immutable._
Now for the answers!
Using breakOut (1 line of code)
Use breakOut - but it involves some unwelcome repetition of types:
scala> res0.map({ case (s, l) => s -> (l.map(o => o.i -> o)(collection.breakOut): SortedMap[Int, O]) })(collection.breakOut): SortedMap[String, SortedMap[Int, O]]
res4: scala.collection.immutable.SortedMap[String,scala.collection.immutable.SortedMap[Int,O]] = Map(a -> Map(1 -> O(1), 2 -> O(2)), b -> Map(2 -> O(2), 3 -> O(3)))
Using a separate method (2 lines of code)
Or a second approach would be to involve a sort method:
scala> def sort[K: Ordering, V](m: Traversable[(K, V)]) = SortedMap(m.toSeq: _ *)
sort: [K, V](m: scala.collection.immutable.Traversable[(K, V)])(implicit evidence$1: Ordering[K])scala.collection.immutable.SortedMap[K,V]
And so:
scala> sort(res0.mapValues(l => sort(l.map(o => o.i -> o)) ))
res13: scala.collection.immutable.SortedMap[java.lang.String,scala.collection.immutable.SortedMap[Int,O]] = Map(a -> Map(1 -> O(1), 2 -> O(2)), b -> Map(2 -> O(2), 3 -> O(3)))

Filter Map by key set

Is there a shortcut to filter a Map keeping only the entries where the key is contained in a given Set?
Here is some example code
scala> val map = Map("1"->1, "2"->2, "3"->3)
map: scala.collection.immutable.Map[java.lang.String,Int] = Map(1 -> 1, 2 -> 2, 3 -> 3)
scala> map.filterKeys(Set("1","2").contains)
res0: scala.collection.immutable.Map[java.lang.String,Int] = Map(1 -> 1, 2 -> 2)
I am searching for something shorter than this.
Answering the Question
You can take advantage of the fact that a Set[A] is a predicate; i.e. A => Boolean
map filterKeys set
Here it is at work:
scala> val map = Map("1" -> 1, "2" -> 2, "3" -> 3)
map: scala.collection.immutable.Map[java.lang.String,Int] = Map(1 -> 1, 2 -> 2, 3 -> 3)
scala> val set = Set("1", "2")
set: scala.collection.immutable.Set[java.lang.String] = Set(1, 2)
scala> map filterKeys set
res0: scala.collection.immutable.Map[java.lang.String,Int] = Map(1 -> 1, 2 -> 2)
Or if you prefer:
scala> map filterKeys Set("1", "2")
res1: scala.collection.immutable.Map[java.lang.String,Int] = Map(1 -> 1, 2 -> 2)
Predicates
It's actually really useful to have some wrapper around a predicate. Like so:
scala> class PredicateW[A](self: A => Boolean) {
| def and(other: A => Boolean): A => Boolean = a => self(a) && other(a)
| def or(other: A => Boolean): A => Boolean = a => self(a) || other(a)
| def unary_! : A => Boolean = a => !self(a)
| }
defined class PredicateW
And an implicit conversion:
scala> implicit def Predicate_Is_PredicateW[A](p: A => Boolean) = new PredicateW(p)
Predicate_Is_PredicateW: [A](p: A => Boolean)PredicateW[A]
And then you can use it:
scala> map filterKeys (Set("1", "2") and Set("2", "3"))
res2: scala.collection.immutable.Map[java.lang.String,Int] = Map(2 -> 2)
scala> map filterKeys (Set("1", "2") or Set("2", "3"))
res3: scala.collection.immutable.Map[java.lang.String,Int] = Map(1 -> 1, 2 -> 2, 3 -> 3)
scala> map filterKeys !Set("2", "3")
res4: scala.collection.immutable.Map[java.lang.String,Int] = Map(1 -> 1)
This can be extended to xor, nand etc etc and if you include symbolic unicode can make for amazingly readable code:
val mustReport = trades filter (uncoveredShort ∨ exceedsDollarMax)
val european = {
val Europe = (_ : Market).exchange.country.region == Region.EU
trades filter (_.market ∈: Europe)
}
Sorry, not a direct answer to your question, but if you know which keys you want to remove (instead of which ones you want to keep), you could do this:
map -- Set("3")
A tangential tip, in case you are going to follow the PredicateW idea in #oxbow_lakes' answer:
In functional programming, instead of defining ad hoc functions, we aim for more generalized and composable abstractions. For this particular case, Applicative fits the bill.
Set themselves are functions, and the Applicative instance for [B]Function1[A, B] lets us lift functions to context. In other words, you can lift functions of type (Boolean, Boolean) => Boolean (such as ||, && etc.) to (A => Boolean, A => Boolean) => (A => Boolean). (Here you can find a great explanation on this concept of lifting.)
However the data structure Set itself has an Applicative instance available, which will be favored over [B]Applicative[A => B] instance. To prevent that, we will have to explicitly tell the compiler to treat the given set as a function. We define a following enrichment for that:
scala> implicit def setAsFunction[A](set: Set[A]) = new {
| def f: A => Boolean = set
| }
setAsFunction: [A](set: Set[A])java.lang.Object{def f: A => Boolean}
scala> Set(3, 4, 2).f
res144: Int => Boolean = Set(3, 4, 2)
And now put this Applicative goodness into use.
scala> val map = Map("1" -> 1, "2" -> 2, "3" -> 3)
map: scala.collection.immutable.Map[java.lang.String,Int] = Map(1 -> 1, 2 -> 2, 3 -> 3)
scala> map filterKeys ((Set("1", "2").f |#| Set("2", "3").f)(_ && _))
res150: scala.collection.immutable.Map[java.lang.String,Int] = Map(2 -> 2)
scala> map filterKeys ((Set("1", "2").f |#| Set("2", "3").f)(_ || _))
res151: scala.collection.immutable.Map[java.lang.String,Int] = Map(1 -> 1, 2 -> 2, 3 -> 3)
scala> map filterKeys (Set("2", "3").f map (!_))
res152: scala.collection.immutable.Map[java.lang.String,Int] = Map(1 -> 1)
Note: All of the above requires Scalaz.