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))
}
Related
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.
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>
This drives me crazy, I can't figure out why this gives me an error.
Here an example of my code:
var seqOfObjects:Seq[Map[String, String]] = Seq[Map[String, String]]()
for(item <- somelist) {
seqOfObjects += Map(
"objectid" -> item(0).toString,
"category" -> item(1),
"name" -> item(2),
"url" -> item(3),
"owneremail" -> item(4),
"number" -> item(5).toString)
}
This gives me an error saying:
Type mismatch, expected: String, actual: Map[String, String]
But a Map[String, String] is exactly what I want to append into my Seq[Map[String, String]].
Why is it saying that my variable seqOfObjects expects a String??
Anyone have a clue?
Thanks
a += b means a = a.+(b). See this answer.
There is no method + in Seq, so you can't use +=.
scala> Seq[Int]() + 1
<console>:8: error: type mismatch;
found : Int(1)
required: String
Seq[Int]() + 1
^
required: String is from string concatenation. This behavior is inherited from Java:
scala> List(1, 2, 3) + "str"
res0: String = List(1, 2, 3)str
Actually method + here is from StringAdd wrapper. See implicit method Predef.any2stringadd.
You could use :+= or +:= instead of +=.
Default implementation of Seq is List, so you should use +: and +:= instead of :+ and :+=. See Performance Characteristics of scala collections.
You could also use List instead of Seq. There is :: method in List, so you can use ::=:
var listOfInts = List[Int]()
listOfInts ::= 1
You can rewrite your code without mutable variables using map:
val seqOfObjects =
for(item <- somelist) // somelist.reverse to reverse order
yield Map(...)
To reverse elements order you could use reverse method.
Short foldLeft example:
sl.foldLeft(Seq[Map[Srting, String]]()){ (acc, item) => Map(/* map from item */) +: acc }
I am new to scala, my objective is to iterate over list and check if items in the list exist the map as its keys and if they exist return the value for that key.
I did the following:
def getMatchingValues(listItmes: List[String]) = {
for (item <- listItems) {
theMap.keys.foreach { i =>
if (i.equals(item)) {
theMap(i)
}
"NoMatch"
}
}
}
I am trying to figure if there is a better way to do this in scala?
Map has a getOrElse method which does what you want:
def getMatchingValues(listItems: List[String]) = listItems map (theMap.getOrElse(_,"NoMatch"))
At least I think that is what you want. Here's an example:
scala> val theMap = Map("a"->"A", "b" -> "B")
theMap: scala.collection.immutable.Map[String,String] = Map(a -> A, b -> B)
scala> val listItems = List("a","b","c")
listItems: List[String] = List(a, b, c)
scala> listItems map (theMap.getOrElse(_,"NoMatch"))
res0: List[String] = List(A, B, NoMatch)
A possible solution with flatMap:
/* Return Some[String] if found, None if not found, then flatten */
def getMatchingValues(listItems: List[String], theMap: Map[String, String]): List[String] =
listItems.flatMap(item => theMap.get(item))
/* Same thing with some syntactic sugar */
def getMatchingValuesSmartass(listItems: List[String], theMap: Map[String, String]): List[String] =
listItems flatMap theMap.get
val l = List("1", "3", "5", "7")
val m = Map("5" -> "five", "2" -> "two", "1" -> "one")
getMatchingValues(l, m)
getMatchingValuesSmartass(l, m)
You could use the map.get method and handle the result with pattern matching
list.map { x => map.get(x) match {
case None => "No match"
case Some(i) => (x, i)
}}
The above code returns a list of pairs where each pair represents the elements of the list and the value associated in the map ("No match" if not found)
If I was you, I would do two steps. Given this Map:
val map = Map("a" -> "b", "b" -> "c", "c" -> "d", "d" -> "e")
and this List:
val list = List("a", "c", "e")
At first I would map an Option value to every item in the List. Giving you if there is a value for the item.
val mapped = list.map(item => item -> map.get(item))
This will give you this:
mapped: List[(String, Option[String])] =
List(("a",Some("b")), ("c",Some("d")), ("e", None))
Calling get on the map returns a wrapped result. If there is a result, you will get the result wrapped in a Some. Otherwise you will get a None. Both are subclasses of Option Option is a closure-construct, that provides you a null-value without having to deal with null. Now you are able to map again, to reach your goal.
val result = mapped.map(tuple => tuple._1 -> tuple._2.getOrElse("No match"))
result: List[(String, String)] = List(("a","b"), ("c","d"), ("e","No match"))
getOrElse extracts the value of a Some or falls back to the parameter if it is a None.
To make it look more professional, we can write this expression in one line ;)
val result = list.map(item => item -> map.get(item).getOrElse("No match"))
This will give you the exact same result.
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).