iterate through values of given key in scala hash map - scala

I need to check all values of a given key to see if the value is already there. With the code i have below i always get the last value added to the key. How to iterate over the entire list of values?
val map = scala.collection.mutable.HashMap.empty[Int, String]
map.put(0, "a")
map.put(0, "b")
map.put(0, "c")
map.put(0, "d")
map.put(0, "e")
map.put(0, "f")
for ((k, v) <- map) {println("key: " + k + " value: " + v)}
output:
map: scala.collection.mutable.HashMap[Int,String] = Map()
res0: Option[String] = None
res1: Option[String] = Some(a)
res2: Option[String] = Some(b)
res3: Option[String] = Some(c)
res4: Option[String] = Some(d)
res5: Option[String] = Some(e)
key: 0 value: f
res6: Unit = ()

A key is unique in a HashMap. You can't have multiple values for the same key. What you can do is have a HashMap[Int, Set[String]] and check if the value is contained inside the set, or even simpler as #TzachZohar points out, a MultiMap:
scala> import collection.mutable.{ HashMap, MultiMap, Set }
import collection.mutable.{HashMap, MultiMap, Set}
scala> val mm = new HashMap[Int, Set[String]] with MultiMap[Int, String]
mm: scala.collection.mutable.HashMap[Int,scala.collection.mutable.Set[String]] with scala.collection.mutable.MultiMap[Int,String] = Map()
scala> mm.addBinding(0, "a")
res9: <refinement>.type = Map(0 -> Set(a))
scala> mm.addBinding(0, "b")
res10: <refinement>.type = Map(0 -> Set(a, b))
scala> mm.entryExists(0, _ == "b")
res11: Boolean = true

Related

Adding an Option[T] to an Option[Seq[T]]

Having an Option[Seq[String]], I'm trying to find the best way to add an Option[String] to that collection, resulting again in an Option[Seq[String]]. If the collection is None, but the value to add is Some[String], the result should be Option[Seq[String]] with that single value.
I've come up with two ways to write this, which essentially boil down to the same. I feel like there must be a better way to do this. Any suggestions?
val messages: Option[Seq[String]] = Some(Seq("item"))
val message: Option[String] = Some("item 2")
val opt1: Option[Seq[String]] = message match {
case Some(message) => Some(messages.map(_ :+ message).getOrElse(Seq(message)))
case None => messages
}
val opt2: Option[Seq[String]] = message
.map(msg => Some(messages.map(_ :+ msg).getOrElse(Seq(msg))))
.getOrElse(messages)
println(opt1) // Some(List(item, item 2))
println(opt2) // Some(List(item, item 2))
When messages are available. Convert message to Seq and add it to existing message.
Else convert message to Option[Seq[String]]
messages.map(msgs => msgs ++ message.toSeq)
.orElse(message.map(x => Seq(x)))
clear syntax
messages.map(_ ++ message.toSeq) orElse message.map(Seq(_))
Scala REPL
scala> :paste
// Entering paste mode (ctrl-D to finish)
def addOption[T](messages: Option[Seq[T]], message: Option[T]): Option[Seq[T]] =
messages.map(msgs => msgs ++ message.toSeq)
.orElse(message.map(Seq(_)))
// Exiting paste mode, now interpreting.
addOption: [T](messages: Option[Seq[T]], message: Option[T])Option[Seq[T]]
scala> addOption(Some(Seq(1)), Some(2))
res4: Option[Seq[Int]] = Some(List(1, 2))
scala> addOption(Some(Seq(1)), Some(3))
res5: Option[Seq[Int]] = Some(List(1, 3))
scala> addOption(Some(Seq(1)), None)
res6: Option[Seq[Int]] = Some(List(1))
scala> addOption(None, None)
res7: Option[Seq[Nothing]] = None
scala> addOption(None, Some(1))
res8: Option[Seq[Int]] = Some(List(1))
scala> addOption(None, Some(2))
res9: Option[Seq[Int]] = Some(List(2))
Here is another one:
def push[T](message: Option[T], messages: Option[Seq[T]]): Option[Seq[T]] =
message.map(s => messages.getOrElse(Nil) :+ s).orElse(messages)
which produces:
push(Some("!"), Some(Seq("hello", "world"))) // Some(List(hello, world, !))
push(None, Some(List("hello", "world"))) // Some(List(hello, world))
push(Some("!"), None) // Some(List(!))
push(None, None) // None
Using fold opt2 becomes:
val opt2: Option[Seq[String]] =
message.fold(messages)(msg => Some(messages.fold(Seq(msg))(_ :+ msg)))
[ Automatic conversion by Intellij :) ]
Here's an approach using fold:
def addOptMsg[T](msgs: Option[Seq[T]], msg: Option[T]): Option[Seq[T]] =
msgs.fold( msg.map(Seq(_)) )( s => Option(s ++ msg.toSeq) )
Testing the method:
val messages: Option[Seq[String]] = Some(Seq("item1", "item2"))
val message: Option[String] = Some("item3")
val messages0: Option[Seq[String]] = None
val message0: Option[String] = None
addOptMsg(messages, message)
// res1: Option[Seq[String]] = Some(List(item1, item2, item3))
addOptMsg(messages, message0)
// res2: Option[Seq[String]] = Some(List(item1, item2))
addOptMsg(messages0, message)
// res3: Option[Seq[String]] = Some(List(item3))
addOptMsg(messages0, message0)
// res4: Option[Seq[String]] = None

Scala: Append only Some's to immutable list

So say we're given a List[String] and bunch of Option[String]'s call them a, b, c. Say I want to append the valid (Some's) Options[String]'s out of a, b, c to my existingList[String]. What would be the best way to go about this using immutable structures?
I.e. I know I could use a ListBuffer and do something like:
def foo(a: Option[String], b: Option[String], c: Option[String]) : ListBuffer[String] = {
val existingList = new ListBuffer("hey")
a.map(_ => existingList += _)
b.map(_ => existingList += _)
c.map(_ => existingList += _)
}
but I want to use immutable structures.
Use .flatten on a list of options and append it to your list
val existingList = List(1, 2, 3)
val a = Some(4)
val b = None
val c = Some(5)
val newList = existingList ::: List(a, b, c).flatten
def foo(a: Option[String], b: Option[String], c: Option[String]): List[String] =
List("hey") ++ a.toList ++ b.toList ++ c.toList
which is similar to flatten or flatMap.
scala> foo(Some("a"), None, Some("c"))
res1: List[String] = List(hey, a, c)
It's better to define a generic function like this:
def foo[T](xs: Option[T]*) : List[T] =
xs.toList.flatten
scala> foo(Some("a"), None, Some("c"))
res2: List[String] = List(a, c)
Let val list = List("A", "B", "C") and val opts = = List(Some("X"), None, Some("Y"), None, Some("Z")). Then list ++ opts.filter(_.isDefined).map(_.get) will give an new List("A", "B", "C", "X", "Y", "Z") with all elements from list and all non-empty elements of opts.

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

How would I yield an immutable.Map in Scala?

I have tried this but it does not work:
val map:Map[String,String] = for {
tuple2 <- someList
} yield tuple2._1 -> tuple2._2
How else would I convert a List of Tuple2s into a Map?
It couldn't be simpler:
Map(listOf2Tuples: _*)
using the apply method in Map companion object.
My First try is this:
scala> val country2capitalList = List("England" -> "London", "Germany" -> "Berlin")
country2capitalList: List[(java.lang.String, java.lang.String)] = List((England,London), (Germany,Berlin))
scala> val country2capitalMap = country2capital.groupBy(e => e._1).map(e => (e._1, e._2(0)._2))
country2capitalMap: scala.collection.Map[java.lang.String,java.lang.String] = Map(England -> London, Germany -> Berlin)
But here is the best solution:
scala> val betterConversion = Map(country2capitalList:_*)
betterConversion: scala.collection.immutable.Map[java.lang.String,java.lang.String] = Map(England -> London, Germany -> Berlin)
The :_* is needed to give the compiler a hint to use the list as a varargs argument. Otherwise it will give you:
scala> Map(country2capitalList)
<console>:6: error: type mismatch;
found : List[(java.lang.String, java.lang.String)]
required: (?, ?)
Map(country2capitalList)
^
From Scala 2.8 on you can use toMap:
scala> val someList = List((1, "one"), (2, "two"))
someList: List[(Int, java.lang.String)] = List((1,one), (2,two))
scala> someList.toMap
res0: scala.collection.immutable.Map[Int,java.lang.String] = Map((1,one), (2,two))
In 2.8, you can use the toMap method:
scala> val someList = List((1, "one"), (2, "two"))
someList: List[(Int, java.lang.String)] = List((1,one), (2,two))
scala> someList.toMap
res0: scala.collection.immutable.Map[Int,java.lang.String] = Map((1,one), (2,two))
This will work for any collection of pairs. Note that the documentation has this to say about its duplicate policy:
Duplicate keys will be overwritten by
later keys: if this is an unordered
collection, which key is in the
resulting map is undefined.
In scala 2.8:
scala> import scala.collection.breakOut
import scala.collection.breakOut
scala> val ls = List("a","bb","ccc")
ls: List[java.lang.String] = List(a, bb, ccc)
scala> val map: Map[String,Int] = ls.map{ s => (s,s.length) }(breakOut)
map: Map[String,Int] = Map((a,1), (bb,2), (ccc,3))
scala> val map2: Map[String,Int] = ls.map{ s => (s,s.length) }.toMap
map2: Map[String,Int] = Map((a,1), (bb,2), (ccc,3))
scala>