Using find function for maps in scala - 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>

Related

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.

applying partial function on a tuple field, maintaining the tuple structure

I have a PartialFunction[String,String] and a Map[String,String].
I want to apply the partial functions on the map values and collect the entries for which it was applicaple.
i.e. given:
val m = Map( "a"->"1", "b"->"2" )
val pf : PartialFunction[String,String] = {
case "1" => "11"
}
I'd like to somehow combine _._2 with pfand be able to do this:
val composedPf : PartialFunction[(String,String),(String,String)] = /*someMagicalOperator(_._2,pf)*/
val collected : Map[String,String] = m.collect( composedPf )
// collected should be Map( "a"->"11" )
so far the best I got was this:
val composedPf = new PartialFunction[(String,String),(String,String)]{
override def isDefinedAt(x: (String, String)): Boolean = pf.isDefinedAt(x._2)
override def apply(v1: (String, String)): (String,String) = v1._1 -> pf(v1._2)
}
is there a better way?
Here is the magical operator:
val composedPf: PartialFunction[(String, String), (String, String)] =
{case (k, v) if pf.isDefinedAt(v) => (k, pf(v))}
Another option, without creating a composed function, is this:
m.filter(e => pf.isDefinedAt(e._2)).mapValues(pf)
There is a function in Scalaz, that does exactly that: second
scala> m collect pf.second
res0: scala.collection.immutable.Map[String,String] = Map(a -> 11)
This works, because PartialFunction is an instance of Arrow (a generalized function) typeclass, and second is one of the common operations defined for arrows.

wrapping different datatype into a generic, in scala

I expect to return a map containing value of different datatypes such as
(key -> String) and (key -> Int), but i can have Map either of
Map[String,String] or Map[String,Int].
I can't use class because number and order of keys are not fixed.
Is there any way to wrap String and Int to a generic class so that i can return map as Map[String,Any]
You can use HMap as #Harnish suggested, but there is an alternative in the scala library: Map[String, Either[Int, String]]. It applies only if you know that the types either one or another and nothing more.
The type Either[Int, String] can be created either by Left(5) or Right("Hello"). Then you can use match to test the value:
x match {
case Left(n) => println(s"$n is a number")
case Right(s) => println(s"$s is a string")
}
Updated
Example:
val dict = scala.collection.mutable.Map[String, Either[String, Int]]()
dict += ("a" -> Right(5))
dict += ("b" -> Left("Hello"))
dict map {
case (key, Right(n)) => println(s"For $key: $n is integer")
case (key, Left(s)) => println(s"For $key: $s is string")
}
I'm not sure if you can do this with the standard collections library, however it is possible using shapeless HMap (Heterogenous map). This is the example given in the docs, which closely matches what you have described:
// Key/value relation to be enforced: Strings map to Ints and vice versa
class BiMapIS[K, V]
implicit val intToString = new BiMapIS[Int, String]
implicit val stringToInt = new BiMapIS[String, Int]
val hm = HMap[BiMapIS](23 -> "foo", "bar" -> 13)
//val hm2 = HMap[BiMapIS](23 -> "foo", 23 -> 13) // Does not compile
scala> hm.get(23)
res0: Option[String] = Some(foo)
scala> hm.get("bar")
res1: Option[Int] = Some(13)
Note, it doesn't give you an Any, instead you have to specify what is valid in your key/value pairs. I'm not sure if that's helpful to you or not...

Scala: Count Words

I am trying to write the function of countWords(ws) that counts the frequency of words in a list of words ws returning a map from words to occurrences.
that ws is a List[String], using the List data type I should produce a Map[String,Int] using the Map data type. an example of what should the function do:
def test{
expect (Map("aa" -> 2, "bb" -> 1)) {
countWords(List("aa", "bb"))
}
}
This is just a perpetration for a test and its not an assignment. I have been stuck on this function for while now. This is what I have so far:
object Solution {
// define function countWords
def countWords(ws : List[String]) : Map[String,Int] = ws match {
case List() => List()
}
}//
which gives type mismatch. I am not quite sure how to use the scala Map Function, for example when ws is Empty list what should it return that passed by Map[String,Int] I have been trying, and thats why I post it here to get some help. thank you.
Another way to do it is using groupBy which outputs Map(baz -> List(baz, baz, baz), foo -> List(foo, foo), bar -> List(bar)). Then you can map the values of the Map with mapValues to get a count of the number of times each word appears.
scala> List("foo", "foo", "bar", "baz", "baz", "baz")
res0: List[String] = List(foo, foo, bar, baz, baz, baz)
scala> res0.groupBy(x => x).mapValues(_.size)
res0: scala.collection.immutable.Map[String,Int] = Map(baz -> 3, foo -> 2, bar -> 1)
Regarding the type mismatch in your program countWords is expecting a Map[String, Int] as the return type and the first(and only) match you have returns an empty List with type Nothing. If you change the match to case List() => Map[String, Int]() it will no longer give a type error. It also gives a warning about an in-exhaustive pattern match obviously won't return the correct output.
The easiest solution is this
def countWords(ws: List[String]): Map[String, Int] = {
ws.toSet.map((word: String) => (word, ws.count(_ == word))).toMap
}
But it's not the fastest one since it searches through the list several times.
edit:
The fastest way is to use a mutable HashMap
def countWords(ws: List[String]): Map[String, Int] = {
val map = scala.collection.mutable.HashMap.empty[String, Int]
for(word <- ws) {
val n = map.getOrElse(word, 0)
map += (word -> (n + 1))
}
map.toMap
}
Use fold to go through your list starting with an empty map
ws.foldLeft(Map.empty[String, Int]){
(count, word) => count + (word -> (count.getOrElse(word, 0) + 1))
}

Get value with the lowest key value from Map[Int, String]

Say I have a map: Map[Int, String]. How would I get the value [String] with the lowest key [Int]. I've been trying to implement this functionally, but just can't figure out how to do this.
The following code will get you a value with a lowest key (ignoring some corner cases).
def lowestKeyMember[A](m: Map[Int,A]): A = m(m.keys.min)
This will break ties arbitrarily and throw on an empty map. If you need to do this operation frequently and/or on large maps, you should look into SortedMap.
Maps are not normally sorted. You could however use a SortedMap, then the map will be sorted and the first value will be the head. All you need to do is retrieve the head.
map.head()
Come on, people! "Functionally" is code word for "folding".
scala> val m = Map(1->"eins",2->"zwei",3->"drei")
m: scala.collection.immutable.Map[Int,String] = Map(1 -> eins, 2 -> zwei, 3 -> drei)
scala> m.foldLeft(Int.MaxValue -> "") { case (min,p) => if (min._1 <= p._1) min else p }
res0: (Int, String) = (1,eins)
But an 8-char operator?
Let's see, is that enough parens? Don't tell me -> is like - and /: is like /.
scala> (Int.MaxValue -> "" /: m) { case (min,p) => if (min._1 <= p._1) min else p }
<console>:9: error: missing arguments for method /: in trait TraversableOnce;
follow this method with `_' if you want to treat it as a partially applied function
(Int.MaxValue -> "" /: m) { case (min,p) => if (min._1 <= p._1) min else p }
^
Oh, well, OK.
scala> ((Int.MaxValue -> "") /: m) { case (min,p) => if (min._1 <= p._1) min else p }
res2: (Int, String) = (1,eins)
Or,
scala> import math.Ordering.Implicits._
import math.Ordering.Implicits._
scala> ((Int.MaxValue -> "") /: m) { case (min,p) if min <= p => min case (_, p) => p }
res5: (Int, String) = (1,eins)
A variant of the _.keys.min solution that works with Options (i.e. will not throw on an empty map):
scala> val a : Map[Int, String]=Map(1 -> "1", 2 -> "2")
a: Map[Int,String] = Map(1 -> 1, 2 -> 2)
scala> val b : Map[Int, String]=Map()
b: Map[Int,String] = Map()
scala> def valueForMinKey[K,V](a : Map[K,V])(implicit cmp : Ordering[K]) = a.keys.reduceOption(cmp.min(_, _)).map(a(_))
valueForMinKey: [K, V](a: Map[K,V])(implicit cmp: Ordering[K])Option[V]
scala> valueForMinKey(a)
res27: Option[String] = Some(1)
scala> valueForMinKey(b)
res28: Option[String] = None
In this example, the implicit parameter cmp will be satisfied by Ordering.Int. The example will work with any Map where the keys can be ordered (and a matching implict can be found by the compiler).