Scala convert Map[String, Set[String]] to Map[(String, String), Option[Double]] - scala

I was thinking of a way to create a tuple consisting of the String key from the map along with each of the Strings from the Set together as tuple that form the key in a new map. Value for the new map will be initialized to 0.0.
Ex: If I have to following:
Map[ USA, Set[CA, NY, WA]]
I want to create a new map from this which looks like:
Map[(USA,CA) -> 0.0, (USA,NY) -> 0.0, (USA,WA) -> 0.0]
I am able to create a Map[String, String] but I was hoping to get some help in creating the tuple key.

Map("USA" -> Set("CA", "NY", "WA")) flatMap { case (k, set) => set.map((k, _) -> 0.0) }

val myMap = Map("USA" -> Set("CA", "NY", "WA"))
val newMap = myMap.foldLeft(Map[(String, String), Double]()) {
case (acc, (key, values)) => {
acc ++ (for {
value <- values
} yield (key, value) -> 0.0)
}
}

Related

Filter defined fields of Options in scala

I have a case class with Options:
case class PersonUpdate(name: Option[String], age: Option[Int], country: Option[String])
and I need to check which values are defined and generate a map with its name and values, for example:
if I have this object:
val perUpdate = PersonUpdate(Option("john"), None, Option("England"))
than the map should look like:
val result = Map("people.$.name" -> "john", "people.$.country" -> "England")
what would be the best way do that efficiently the scala way?
For your specific case you can do this:
List(
perUpdate.name.map("people.$.name" -> _),
perUpdate.age.map("people.$.age" -> _.toString),
perUpdate.country.map("people.$.country" -> _)
).flatten.toMap
You can have a more generic solution, but it is not going to be particularly efficient:
perUpdate.getClass.getDeclaredFields.flatMap { f =>
f.setAccessible(true)
f.get(perUpdate).asInstanceOf[Option[Any]].map("people.$."+f.getName -> _.toString)
}.toMap
To extract only certain fields, try this:
val fieldNames = List("name", "age", "country")
fieldNames.flatMap{ fieldName =>
val fieldValue = perUpdate.getClass.getDeclaredField(fieldName)
fieldValue.setAccessible(true)
fieldValue.get(perUpdate).asInstanceOf[Option[Any]].map("people.$."+fieldName -> _.toString)
}.toMap
Case classes are instances of Product, that lets you iterate through their members without reflection:
Seq("name", "age", "country")
.map { "people.$." + _ }
.iterator
.zip(perUpdate.productIterator)
.collect { case (k, Some(v)) => k -> v }
.toMap

How to merge Maps in Scala with tuples as key

I have this initial kind of maps:
m: Map[(String, String, String), Double]
and I would like to merge them in a way to get a final Map with the following type:
mm: Map[(String, String, String), Seq[Double]]
So for example:
val m1 = Map (("a","b","c") -> 2.0, ("a","b","d") -> 3.0)
val m2 = Map (("a","b","c") -> 5.0, ("a","b","k") -> 3.0)
// after the merge
Map (("a","b","c") -> Seq(2.0, 5.0), ("a","b","d") -> Seq(3.0), ("a","b","k") -> Seq(3.0))
How can I get that with Scala?
You can do:
(m1.toSeq ++ m2.toSeq)
.groupBy { case (k, v) => k }
.mapValues(_.map { case (k, v) => v })
If you have already imported scalaz then you can do:
m1.mapValues(_.point[List]) |+| m2.mapValues(_.point[List])
You can convert Maps to Seq and then group the Seq by the key:
(m1.toSeq ++ m2.toSeq).groupBy(_._1).mapValues(_.map(_._2))
// res80: scala.collection.immutable.Map[(String, String, String),Seq[Double]] = Map((a,b,k) -> ArrayBuffer(3.0), (a,b,c) -> ArrayBuffer(2.0, 5.0), (a,b,d) -> ArrayBuffer(3.0))

Scala Map Reduction and Aggregation

I have a Map that looks like this and is of Type Map[String, Seq[String]]
Map(
"5" -> Seq("5.1"),
"5.1" -> Seq("5.1.1", "5.1.2"),
"5.1.1" -> Seq("5.1.1.1"),
"5.1.2" -> Seq.empty[String],
"5.1.1.1" -> Seq.empty[String]
)
Given a key, I would like to fetch all the values recursively that belongs to the given key. Say for example., if I want to look up for the key 5, I expect the result to be:
Given Input is: 5
Expected Output is: Seq(5.1, 5.1.1, 5.1.2, 5.1.1.1)
Here is what I tried so far:
def fetchSequence(inputId: String, acc: Seq[String], seqMap: Map[String, Seq[String]]): Seq[String] = seqMap.get(inputId) match {
case None => acc
case Some(subSeq) =>
val newAcc = acc ++ subSeq
subSeq.collect {
case subId=> fetchSequence(subId, newAcc, seqMap)
}.flatten
}
I get an empty result when I call fetchSequence with the Map that I have above.
Somewhat more concise :
def recGet[A](map: Map[A, Seq[A]])(key: A): Seq[A] =
map.get(key).fold(
// return empty Seq if key not found
Seq.empty[A])(
// return a Seq with
// the key and
// the result of recGet called recursively
// (for all the elements in the Seq[A] found for key)
x => Seq(key) ++ x.flatMap(recGet(map)))
You can use recGet as :
val sections = Map(
"5" -> Seq("5.1"),
"5.1" -> Seq("5.1.1", "5.1.2"),
"5.1.1" -> Seq("5.1.1.1"),
"5.1.2" -> Seq.empty[String],
"5.1.1.1" -> Seq.empty[String]
)
recGet(sections)("5") // Seq[String] = List(5, 5.1, 5.1.1, 5.1.1.1, 5.1.2)
recGet(sections)("5.1.1") // Seq[String] = List(5.1.1, 5.1.1.1)
recGet(sections)("5.2") // Seq[String] = List()
This will also give you the (first) element itself (if it exists in the map), if you don't want that, you can probably wrap recGet in another method which uses drop(1) on the result of recGet.

In Scala, given a list of lists, how can I create one nested HashMap from the elements?

In Scala, given a list of lists, how can I create one nested HashMap from the elements? I would like to create the HashMap as a hierarchical tree such that for an element at index i, the element at index i - 1 is its parent.
Example for lists of known length:
val lst = List (
List(34, 56, 78),
List(34, 56,79),
List (87, 23, 12),
List(87, 90, 78),
List(1, 45, 87)
)
scala> lst.groupBy(l => l(0))
.mapValues(l => l.groupBy(x => x(1)))
.mapValues{ case x => x.mapValues(y => y.map (z => z(2))) }
res2: scala.collection.immutable.Map[Int,scala.collection.immutable.Map[Int,List[Int]]] = Map(34 -> Map(56 -> List(78, 79)), 1 -> Map(45 -> List(87)), 87 -> Map(23 -> List(12), 90 -> List(78)))
This method works when the length of the elements are known but does not work for an arbitrary length N. Is there any solution that can create this nested map for lists of any length where every list has the same length?
Some preliminary tests seem to indicate that this might work.
def nest(lli: List[List[Int]]): Traversable[_] =
if (lli.head.size == 1)
lli.flatten.distinct
else
lli.groupBy(_.head)
.mapValues(vs => nest(vs.map(_.tail)))
private def buildPartitionTree(partitionValues: List[List[Any]]): Map[Any, Any] = {
val valuesAsNestedMaps = partitionValues.map(_.foldRight(Map[Any,Map[Any,_]]()) { case (partitionValue, map) =>
Map(partitionValue.toString -> map)
}).map(_.asInstanceOf[Map[Any, Any]])
valuesAsNestedMaps.reduce[Map[Any, Any]] { case (map1: Map[Any, Any], map2: Map[Any, Any]) => mergeMaps(map1, map2) }
}
private def mergeMaps(map1 : Map[Any, Any], map2 : Map[Any, Any]) = (map1.keySet ++ map2.keySet).map(key =>
key -> mergeMapValues(map1.get(key), map2.get(key))
).toMap
private def mergeMapValues(o1 : Option[Any], o2 : Option[Any]): Any = (o1, o2) match {
case (Some(v1: Map[Any, Any]), Some(v2: Map[Any, Any])) => mergeMaps(v1, v2)
case (None, Some(x)) => x
case (Some(y), None) => y
}
val nestedMap = buildPartitionTree(lst)
Since the size of sublists is arbitrary you cannot specify the result type of desired function. Consider introducing recursive data structure like this:
trait Tree[A]
case class Node[A](key:A, list:List[Tree[A]]) extends Tree[A]
case class Leaf[A](value:A) extends Tree[A]
Now you can create function producing desired result in terms of trees:
def toTree[A](key:A, list:List[List[A]]):Tree[A] =
if (list.exists(_.isEmpty)) Leaf(key)
else Node(key, list.groupBy(_.head).map {case (k,v) => toTree(k, v.map(_.tail))}.toList)
Since you don't have 'root' value for key, you can call toTree function with some fake key:
toTree(-1, lst)
res1: Node(-1,List(Node(34,List(Node(56,List(Leaf(79), Leaf(78))))), Node(1,List(Node(45,List(Leaf(87))))), Node(87,List(Node(23,List(Leaf(12))), Node(90,List(Leaf(78)))))))

Using find function for maps in scala

I am trying to find a key in a Map, given a value. I am using the 'find' function by not able to figure out the right predicate for it:
val colors = Map(1 -> "red", 2 -> "blue")
def keyForValue(map: Map[Int, String], value: String) = {
val bool = map.find{map.foreach{map.values(i) == value}}
bool.key
}
How do I iterate over the map and find the key when I know the value?
You use the same kind of predicate as with a List, but keep in mind you're evaluating it over (key,value) pairs, instead of just values (and getting a pair back as well!).
Simple example:
val default = (-1,"")
val value = "red"
colors.find(_._2==value).getOrElse(default)._1
The signature for find in Map is find(p: ((A, B)) ⇒ Boolean): Option[(A, B)]. So the predicate takes a Tuple2 and must return a Boolean. Note I changed value to an Int since the key in colors is also an Int.
scala> def keyForValue(map: Map[Int, String], value: Int) = {
| colors.find({case (a,b) => a == value})
| }
keyForValue: (map: Map[Int,String], value: Int)Option[(Int, String)]
Test:
scala> keyForValue(colors, 1)
res0: Option[(Int, String)] = Some((1,red))
You can also use get:
scala> colors.get(1)
res1: Option[String] = Some(red)
You can always go with the abstract solution and swap the keys with their values, store it in a new map, and then search the new map:
val colors = Map(1 -> "red", 2 -> "blue")
def keyForValue(map: Map[Int, String], value: String) = {
val revMap = map map {_.swap}
val key = revMap(value)
key
}
The third line swaps the keys with their values, and stores it in revMap. (map map means the name of the map, in this case, the parameter, map, then the word map, then {_.swap} to actually swap the keys with their values.
I would avoid passing the map to the find method, and rather pass only the map's keys to the find method.
This avoids dealing with an Option[Int,String] -- instead it is Option[Int].
// sample data
val colors = Map(1 -> "red", 2 -> "blue", 3 -> "yellow")
// function you need
def keyForValue(theMap: Map[Int, String], theValue: String): Int = {
val someKey = theMap.keys.find( k => theMap(k) == theValue )
someKey match {
case Some(key) => {
println(s"the map contains ${key} -> ${theValue}")
return key
}
case None => {
println(s"a key was not found for ${theValue}")
return -1
}
}
}
This gives:
scala> val result = keyForValue( colors, "blue" )
the map contains 2 -> blue
result: Int = 2
scala>