Adding to scala mutable map fails - scala

I don't understand why += works when I define a tuple mapEntry beforehand and then add it to a map, while trying to add directly bypassing creating of unnecessary variable mapEntry fails. Most likely I am missing something obvious. Thanks in advance.
This works: map += mapEntry while
this fails: map += ("key-to-tom", Person("Tom", "Marx"))
Here is my complete scala REPL session
Welcome to Scala version 2.10.3 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0_45).
Type in expressions to have them evaluated.
Type :help for more information.
scala> case class Person(val fname:String, val lname:String)
defined class Person
scala> val map = scala.collection.mutable.Map[String,Person]()
map: scala.collection.mutable.Map[String,Person] = Map()
scala> val mapEntry = ("key-to-joe", Person("Joe", "Smith") )
mapEntry: (String, Person) = (key-to-joe,Person(Joe,Smith))
scala> map += mapEntry
res0: map.type = Map(key-to-joe -> Person(Joe,Smith))
scala> val mapEntry2 = ("key-to-ann", Person("Ann", "Kline") )
mapEntry2: (String, Person) = (key-to-ann,Person(Ann,Kline))
scala> map += mapEntry2
res1: map.type = Map(key-to-joe -> Person(Joe,Smith), key-to-ann -> Person(Ann,Kline))
scala> map += ("key-to-tom", Person("Tom", "Marx") )
<console>:11: error: type mismatch;
found : String("key-to-tom")
required: (String, Person)
map += ("key-to-tom", Person("Tom", "Marx") )
^
scala>

Your last statement fails because you need to enclose the tuple in parentheses to convey that you are adding a tuple:
map += ( ("key-to-tom", Person("Tom", "Marx")) )
The problem is that += is overloaded. Aside from a single tuple, += also can take two or more arguments. The signature is this:
def +=(elem1: (A, B), elem2: (A, B), elems: (A, B)*): Map.this.type
Your tuple is a Tuple2 (which is the same as a Pair but still just a single parameter), and this overloaded method takes 2 arguments (at least). Scala chooses the latter as the closer match.

Related

How to increment an Int value in a Map with getOrElseUpdate?

I can update a mutable.Map value with +=:
scala> val map = mutable.Map[Int,Int]()
map: scala.collection.mutable.Map[Int,Int] = Map()
scala> map(5) = 5
scala> map
res55: scala.collection.mutable.Map[Int,Int] = Map(5 -> 5)
scala> map(5) += 1
scala> map
res57: scala.collection.mutable.Map[Int,Int] = Map(5 -> 6)
But I can't use += with getOrElseUpdate and I don't understand why:
scala> map.getOrElseUpdate(5, 0) += 1
<console>:16: error: value += is not a member of Int
Expression does not convert to assignment because receiver is not assignable.
map.getOrElseUpdate(5, 0) += 1
^
I know I've used a mutable.SortedMap with mutable.ArrayBuffer values before and it let me use that type's += operation with getOrElseUpdate with no problem. Is there something like mutable.Int that I'm supposed to use?
In scala you don't get semantics of obtaining "reference" of a variable and it is because good style of scala is when you don't mutate variables on your own, so you can't do this in this way. Rather than this you can describe mutation with function rather than directly mutating variable in this way:
import scala.collection.mutable
val map = mutable.Map.empty[Int,Int]
val yourKey: Int = ???
map.updateWith(yourKey){
case Some(i) => Some(i+1)
case None => Some(1)
}
Here i used this: https://www.scala-lang.org/api/2.13.6/scala/collection/mutable/Map.html#updateWith(key:K)(remappingFunction:Option[V]=%3EOption[V]):Option[V]
Also +, += and +: functions in array buffer and other mutable collections have other semantics.

Scala: a strange bahaviour of seqAsJavaList

Given the following code:
import collection.JavaConversions._
def j(x: java.util.List[java.lang.Integer]): Unit =
// the function body here can be any valid code
println (x)
val a = List(1)
I get a type mismatch error when I call this:
j (seqAsJavaList(a))
Here is the error from REPL
<console>:13: error: type mismatch;
found : List[Int]
required: Seq[Integer]
f (seqAsJavaList(a))
^
However, I can call this with no error:
j(seqAsJavaList(List(1)))
I'm using 2.11.2.
Can someone explain to me why seqAsJavaList behaves differently? Thanks.
Adding more context/clarification to my original question:
What I meant to ask was "why does seqAsJavaList behave differently when operating on a predefined variable a than on an in-place value List(1) when they are of the same type?" Furthermore seqAsJavaList(a) and seqAsJavaList (List(1)) return exactly the same signature java.util.List[Int]. Using the substitution model, one would expect both j (seqAsJavaList(a)) and j (seqAsJavaList (List(1)) ) to succeed. And yet, only the latter works. When both seqAsJavaList(a) and seqAsJavaList (List(1)) are java.util.List[Int], why does one usage handle Int well and the other doesn't?
Another note:
I just tried collection.JavaConverters and the result is not ideal but at least consistent:
// The definitions of j & a are the same as above. I repeat them here to save some page scrolling.
// BTW, instead of f, I use j to indicate it is supposed to be a Java static method.
// I mock it in Scala so that this can be easily run in REPL.
def j ( ls: java.util.List [java.lang.Integer] ): Unit = println (ls)
val a = List( 1 )
// new code here
import collection.JavaConverters._
// Both require the explicit casting to work
j ( a.map (i => i: java.lang.Integer).asJava )
j ( List(1).map (i => i: java.lang.Integer).asJava )
// These fail with the same error.
j( a.asJava )
j( List(1).asJava )
// <console>:12: error: type mismatch;
// found : java.util.List[Int]
// required: java.util.List[Integer]
// j ( List(1).asJava )
// ^
The problem here is not with List but with its type Int. For example this works:
scala> j ( seqAsJavaList (a.map(x => x:Integer)) )
[1]
j expects argument type as java.util.List [java.lang.Integer]. But the return type of seqAsJavaList in your case is java.util.List [Int].
The above example works because now seqAsJavaList will take List [java.lang.Integer] and return java.util.List[java.lang.Integer]. Hence it works.
Or you could:
scala> implicit def toJavaIntegerList(ls:List[Int]):java.util.List[Integer] = seqAsJavaList(ls.map(x => x:Integer))
scala> j(List(1,2,3))
[1, 2, 3]
To explain why this works:
j (seqAsJavaList(List(1)))
This is equivalent to:
scala> val temp:List[Integer] = List(1)
temp: List[Integer] = List(1)
scala> j (seqAsJavaList(temp))
[1]
Or better: j (seqAsJavaList(List(1:Integer)))
Type-inference is in work here. There is an implicit function
implicit def int2Integer(x:Int):java.lang.Integer
defined in Predef. When you do j(seqAsJavaList(List(1))), type inference predicts that this can succeed legitimately by using implicit function which converts Int => java.lang.Integer. It sees that if this implicit is used with all elements of List, then call will succeed legitimately. So List(1) is actually constructed as List[Integer] rather than List[Int]
This is confirmed by checking
object Temp extends App{
import collection.JavaConversions._
def j(x: java.util.List[java.lang.Integer]): Unit = println (x)
j(seqAsJavaList(List(1)))
}
jatinpuri#jatin:~/Desktop$ scalac -Xprint:typer Temp.scala
[[syntax trees at end of typer]] // Temp.scala
package <empty> {
object Temp extends AnyRef with App {
def <init>(): Temp.type = {
Temp.super.<init>();
()
};
import scala.collection.JavaConversions._;
def j(x: java.util.List[Integer]): Unit = scala.this.Predef.println(x);
Temp.this.j(scala.collection.JavaConversions.seqAsJavaList[Integer](immutable.this.List.apply[Integer](scala.this.Predef.int2Integer(1))))
}
}
Notice (immutable.this.List.apply[Integer](scala.this.Predef.int2Integer(1))). So List(1) is actually constructed as List[Integer] and not List[Int]
This doesn't work in original case because in doing val a = List(1), a is set as List[Int]. And the only way to change it to List[Integer] is map all contents to Integer (as there is no implicit available in scala lib that converts List[Int] => List[Integer])

how to dynamically compare a value, such as 2 with "<10", since 2<10, so return true?

I have the following map and list
val a=Map(1->"<10",2->"<20")
val b=List((1,15),(2,15))
get the tuples from b:
use the first element of the tuple to look up the map, and compare the second element, if it matches then pick it up:
e.g
for tuple of (1,15), lookup the map we get "<10", and then compare the second element which 15 with "<10", it failed, so throw this tuple away.
for tuple of (2,15), we will compare 15 with "<20", it's correct, so keep the tuple of (2,14).
Is it doable in scala?
Replace your "<10" and "<20" with functions:
val a = Map[Int, Int => Boolean](1 -> (_ < 10), 2 -> (_ < 20))
And then you can do:
b filter { case (k, v) => a(k)(v) }
Or equivalently:
b filter Function.uncurried(a).tupled
Scala is scriptable as well as scalable. Sort of.
Welcome to Scala version 2.11.0 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0).
Type in expressions to have them evaluated.
Type :help for more information.
scala> val a=Map(1->"<10",2->"<20")
a: scala.collection.immutable.Map[Int,String] = Map(1 -> <10, 2 -> <20)
scala> val b=List((1,15),(2,15))
b: List[(Int, Int)] = List((1,15), (2,15))
scala> import javax.script._
import javax.script._
scala> new ScriptEngineManager().getEngineByName("scala")
res0: javax.script.ScriptEngine = scala.tools.nsc.interpreter.IMain#4ba302e0
scala> b filter { case (k, v) => (res0 eval s"$v${a(k)}").asInstanceOf[Boolean] }
res1: List[(Int, Int)] = List((2,15))

Append Map[String, String] to a Seq[Map[String, String]]

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 }

Why is Seq.newBuilder returning a ListBuffer?

Looking at
val sb = Seq.newBuilder[Int]
println(sb.getClass.getName)
sb += 1
sb += 2
val s = sb.result()
println(s.getClass.getName)
the output is
scala.collection.mutable.ListBuffer
scala.collection.immutable.$colon$colon
using Scala 2.10.1.
I would expect Seq.newBuilder to return a VectorBuilder for example. This is returned by CanBuildFrom, if the result is explicitly typed to a Seq:
def build[T, C <: Iterable[T]](x: T, y: T)
(implicit cbf: CanBuildFrom[Nothing, T, C]): C = {
val b = cbf()
println(b.getClass.getName)
b += x
b += y
b.result()
}
val s: Seq[Int] = build(1, 2)
println(s.getClass.getName) // scala.collection.immutable.Vector
in this case the builder is a VectorBuilder, and the result's class is a Vector.
So I explicitly wanted to build a Seq, but the result is a List which needs more RAM, according to Scala collection memory footprint characteristics.
So why does Seq.newBuilder return a ListBuffer which gives a List in the end?
The Scala Collections API is very complex and its hierarchy is rich in depth. Each level represents some sort of new abstraction. The Seq trait split up into two different subtraits, which give different guarantees for performance (ref.):
An IndexedSeq provides fast random-access of elements and a fast length operation. One representative of this IndexedSeq is the Vector.
A LinearSeq provides fast access only to the first element via head, but also has a fast tail operation. One representative of this LinearSeq is the List.
As the current default implementation of a Seq is a List, Seq.newBuilder will return a ListBuffer. However, if you want to use a Vector you can either use Vector.newBuilder[T] or IndexedSeq.newBuilder[T]:
scala> scala.collection.immutable.IndexedSeq.newBuilder[Int]
res0: scala.collection.mutable.Builder[Int,scala.collection.immutable.IndexedSeq[Int]] = scala.collection.immutable.VectorBuilder#1fb10a9f
scala> scala.collection.immutable.Vector.newBuilder[Int]
res1: scala.collection.mutable.Builder[Int,scala.collection.immutable.Vector[Int]] = scala.collection.immutable.VectorBuilder#3efe9969
The default Seq implementation is List:
Seq(1, 2, 3) // -> List(1, 2, 3)
...thus ListBuffer is the correct builder. If you want Vector, use Vector.newBuilder or IndexedSeq.newBuilder.
OK, but you're not going to believe it. Turning on -Yinfer-debug for your CanBuildFrom counter-example,
[search] $line14.$read.$iw.$iw.build[scala.this.Int, Seq[scala.this.Int]](1, 2) with pt=generic.this.CanBuildFrom[scala.this.Nothing,scala.this.Int,Seq[scala.this.Int]] in module class $iw, eligible:
fallbackStringCanBuildFrom: [T]=> generic.this.CanBuildFrom[String,T,immutable.this.IndexedSeq[T]]
[solve types] solving for T in ?T
inferExprInstance {
tree scala.this.Predef.fallbackStringCanBuildFrom[T]
tree.tpe generic.this.CanBuildFrom[String,T,immutable.this.IndexedSeq[T]]
tparams type T
pt generic.this.CanBuildFrom[scala.this.Nothing,scala.this.Int,Seq[scala.this.Int]]
targs scala.this.Int
tvars =?scala.this.Int
}
[search] considering no tparams (pt contains no tvars) trying generic.this.CanBuildFrom[String,scala.this.Int,immutable.this.IndexedSeq[scala.this.Int]] against pt=generic.this.CanBuildFrom[scala.this.Nothing,scala.this.Int,Seq[scala.this.Int]]
[success] found SearchResult(scala.this.Predef.fallbackStringCanBuildFrom[scala.this.Int], ) for pt generic.this.CanBuildFrom[scala.this.Nothing,scala.this.Int,Seq[scala.this.Int]]
[infer implicit] inferred SearchResult(scala.this.Predef.fallbackStringCanBuildFrom[scala.this.Int], )
and indeed,
implicit def fallbackStringCanBuildFrom[T]: CanBuildFrom[String, T, immutable.IndexedSeq[T]] =
new CanBuildFrom[String, T, immutable.IndexedSeq[T]] {
def apply(from: String) = immutable.IndexedSeq.newBuilder[T]
def apply() = immutable.IndexedSeq.newBuilder[T]
}
What do you mean, your Iterable is not a String?
trait CanBuildFrom[-From, -Elem, +To]
Such is the evil of inferring either Nothing or Any.
Edit: Sorry, I misspoke, I see that you told it Nothing explicitly.
Update:
Since CBF is contravariant in From, a CBF from String serves as a CBF from Nothing.
scala> typeOf[CanBuildFrom[Nothing,Int,Seq[Int]]] <:< typeOf[CanBuildFrom[String,Int,Seq[Int]]]
res0: Boolean = false
scala> typeOf[CanBuildFrom[String,Int,Seq[Int]]] <:< typeOf[CanBuildFrom[Nothing,Int,Seq[Int]]]
res1: Boolean = true
For instance, if you need to build from an immutable.Map, you'd want a CBF from collection.Map to work.
As someone else commented, it's just weird for Nothing. But you get what you asked for. That is, you underspecified, which means you don't mind much what you get back, Vector or whatever.
I agree that this is weird. Why don't you just use Vector.newBuilder, if that's what you're looking for?
scala> val sb = Vector.newBuilder[Int]
sb: scala.collection.mutable.Builder[Int,scala.collection.immutable.Vector[Int]] = scala.collection.immutable.VectorBuilder#1fb7482a
scala> println(sb.getClass.getName)
scala.collection.immutable.VectorBuilder
scala> sb += 1
res1: sb.type = scala.collection.immutable.VectorBuilder#1fb7482a
scala> sb += 2
res2: sb.type = scala.collection.immutable.VectorBuilder#1fb7482a
scala> val s = sb.result()
s: scala.collection.immutable.Vector[Int] = Vector(1, 2)
scala> println(s.getClass.getName)
scala.collection.immutable.Vector