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 }
Related
I am trying to return a Map from a scala method as below.
I have two different Maps with some matching keys. I need to find the matching keys between them and pick the values out of them and put them in another Map in the way I wanted. Below is the code I wrote for the aforementioned action.
val common = rdKeys.keySet.intersect(bounds.keySet).toList
val metaColumns = getReadColumns(common, rdKeys, bounds)
def getReadColumns(common:List[String], rdKeys:scala.collection.mutable.Map[String, String], bounds:scala.collection.mutable.Map[String, String]): scala.collection.mutable.Map[String, String] = {
var metaMap = scala.collection.mutable.Map[String, String]
common.map {
c => metaMap += (c -> bounds(c) + "|" + rdKeys(c))
}
metaMap
}
But the method is giving me a compilations error:
Expression of type Seq[(String, String)] => mutable.Map[String, String] doesn't confirm to expected type mutable.Map[String, String]
All the method parameters, Maps used inside the method & the return type of the method are of mutable.Map[String, String]. I don't understand what is the mistake I did here.
Could anyone let me know what do I have to do to correct the problem ?
You got the error
Expression of type Seq[(String, String)] => mutable.Map[String, String] doesn't confirm to expected type mutable.Map[String, String]
Because of the statement scala.collection.mutable.Map[String, String] returns a function Seq[(String, String)] => mutable.Map[String, String]
You can correct it by empty method:
def getReadColumns( common:List[String],
rdKeys:scala.collection.mutable.Map[String, String],
bounds:scala.collection.mutable.Map[String, String]):scala.collection.mutable.Map[String, String] = {
val metaMap = scala.collection.mutable.Map.empty[String, String]
common.foreach {
c => metaMap.update(c, bounds(c) + "|" + rdKeys(c))
}
metaMap
}
P.S. or use c => metaMap += c -> (bounds(c) + "|" + rdKeys(c))
map preserves the collection type. You can map a List to another List, and, in the end, cast your List directly to a Map
val common = List("a", "b")
val rdKeys = Map("a" -> 1, "b" -> 1)
val bounds = Map("a" -> 10, "b" -> 10)
common // this is a list
.map(c => c -> (bounds(c) + "|" + rdKeys(c))) // this is a list
.toMap // then cast to it to a Map
This code outputs
scala.collection.immutable.Map[String,String] = Map(a -> 10|1, b -> 10|1)
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...
I can't grasp the map method I guess..
Trying to read a file :
val messagesMap = XML.loadFile(messageXMLFile).map(parseMessageXML)
where the method parseMessageXML is defined as :
def parseMessageXML(xml : scala.xml.Node) = {
val nodes = xml \\ "add"
nodes.map({
node =>
val obj = new AdMessage(node)
println("adding an AdMessage " + obj.toString)
(obj.MessageId -> obj)
}).toMap
}
Can anybody please explain why I end up with a Seq[Map[String, AdMessage]] and not a just a Map[String, AdMessage] ?
map transforms each element of your Seq into an another element.
For instance:
scala> Seq("One", "Two", "Three").map(_.length())
res0: Seq[Int] = List(3, 3, 5)
Each String is mapped into an Int thanks to the length function. Therefore the original type is Seq[String] and the final type is Seq[Int]
In your case, parseMessageXML transforms a Node into a Map[String, AdMessage], so the original type is Seq[Node] and the final type is Seq[Map[String, AdMessage]].
In your case, assuming you just want to transform the content of the file into a Map[String, AdMessage]:
val messagesMap = parseMessageXML(XML.loadFile(messageXMLFile))
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))
}
Given a List of JsObject's, I'd like to convert it to a Map[String, JsValue], and then iterate through each map's key-value pair, making a JsObject per pair. Finally, I'd like to put all of these JsObject's into a List[JsObject].
Below is my attempt with a for expression.
def getObjMap(obj: JsObject): Option[Map[String, JsValue]] = obj match {
case JsObject(fields) => Some(fields.toMap)
case _ => None
}
def createFields(x: (String, JsValue)): JsObject = Json.obj("n" -> x._1,
"v" -> x._2)
val objects: List[JsObject] = foo()
val res: List[JsObject] = for {
obj <- objects
map <- getObjMap(obj)
mapField <- map
} yield(createFields(mapField))
However, I'm getting a compile-time error on the mapField <- map line.
[error] ...\myApp\app\services\Test.scala:65: type mismatch;
[error] found : scala.collection.immutable.Iterable[play.api.libs.
json.JsObject]
[error] required: Option[?]
I think the problem is coming from the fact that the for comprehension starts by working over a List, then ends up working over an Option (from getObjMap). The type of construct needs to remain constant across the whole for comprehension. Calling .toList on the result of the getObjMap call fixes this:
val res: List[JsObject] = for {
obj <- objects
map <- getObjMap(obj).toList
mapField <- map
} yield(createFields(mapField))