Understanding this Mutable Recursive Function using the uJson library - scala

I am trying to implement an insert function using the ujson library:
Here is my attempt:
import ujson.{Obj, Value}
import upickle.default._
object Example extends App {
def r = transform(
List(Map(
"a" -> Map("b" -> Obj("c" -> List(1,2,3), "d" -> List(2,4,6))),
"e" -> Map("f" -> Obj("g" -> List(1,2,3)))))
).to(Value)
def insert(j: ujson.Value, k: String, v: ujson.Value): Unit = j match {
case a: ujson.Arr => a.arr.foreach(e => insert(e, k, v))
case o: ujson.Obj =>
if (o.obj.keySet contains k) o.obj(k) = v
else o.obj.values.foreach(e => insert(e, k, v))
case _ => Nil
}
println(r)
insert(r, "b", transform(None).to(Value))
println(r)
}
However, this gives me output that is unchanged:
[{"a":{"b":{"c":[1,2,3],"d":[2,4,6]}},"e":{"f":{"g":[1,2,3]}}}]
[{"a":{"b":{"c":[1,2,3],"d":[2,4,6]}},"e":{"f":{"g":[1,2,3]}}}]
Given that the Value type is mutable, why does this not mutate and update the key, k, with value v for json value object r?

You are creating Value anew every time you call r so, every changes you would make to it, are dismissed.
You create one copy when you call println(r).
Then you create a separate copy with insert(r, "b", transform(None).to(Value)), mutate it and dismiss.
Then you are creating third copy with another println(r).
If you want to refer to the same object use val instead of def.

Related

How to update value of a key in a immutable tree map in Scala

I'm using Scala 2.11 and i'm trying to update the value of a key in the tree map. I tried using updated:
private val xyz = List(0, 100000, 500000, 1000000)
private val abc = List (0, 5, 25, 50)
private var a = TreeMap.empty[Int, TreeMap[Int, Int]] ++ xyz.map {
aa => aa -> (TreeMap.empty[Int, Int] ++ abc.map(bb => bb -> 0))
}
a(xyz(0)).foreach {
case (key, value) =>
if (key < 50) {
a(xyz(0)) = a(xyz(0)).updated(key, 5)
}
}
And got the error:
value update is not a member of scala.collection.immutable.TreeMap[Int,scala.collection.immutable.TreeMap[Int,Int]]
Is it possible to update it? Or could someone please help me replicate the logic using a Java Tree Map since that will also allow me to use floorEntry and ceilingEntry functions. I tried converting to java tree map and it generated a regular map, not a tree map:
private var a = TreeMap.empty[Int, TreeMap[Int, Int]] ++ xyz.map {
aa => aa -> (TreeMap.empty[Int, Int] ++ abc.map(bb => bb -> 0)).asJava
}
private var b = a.asJava
You are getting confused between var/val and mutable/immutable.
I think you correctly understood the difference between val and var, that the former is an immutable variable and later is mutable. ie, if you try to reassign the object assigned as val you will get an error.
import scala.collection.immutable.TreeMap
val tm = TreeMap(1 -> 1, 2 -> 2, 3 -> 3)
tm = TreeMap(1->2)
^
error: reassignment to val
But a var can be mutated:
import scala.collection.immutable.TreeMap
var tm = TreeMap(1 -> 1, 2 -> 2, 3 -> 3)
tm = TreeMap(1->2)
// mutated tm
Notice that in the latter case, even though we are mutating the variable, we are not mutating the collection itself, we are assigning a new TreeMap. As we were using scala.collection.immutable.TreeMap it cant be mutated.
Instead, if we had used scala.collection.mutable.TreeMap, it has an update function
import scala.collection.mutable.TreeMap
val tm = TreeMap(1 -> 1, 2 -> 2, 3 -> 3)
tm.update(1, 5)
tm //TreeMap(1 -> 5, 2 -> 2, 3 -> 3)
Once you change scala.collection.immutable.TreeMap to scala.collection.mutable.TreeMap, this will work
a(xyz(0)).foreach{ case (key, value) =>
if(key < 50){
a(xyz(0)) = a(xyz(0)).updated(key, 5) //addOne(key, 5) if 2.13+
}
}
EDIT using java.util.TreeMap
private val xyz = List(0, 100000, 500000, 1000000)
private val abc = List(0, 5, 25, 50)
import java.util.{TreeMap => JTreeMap}
val jTreeMap = xyz.foldLeft(new JTreeMap[Int, JTreeMap[Int, Int]]()) { (acc, elem) =>
acc.put(
elem,
abc.foldLeft(new JTreeMap[Int, Int]()) { (acc2, elem2) =>
acc2.put(elem2, 0)
acc2
}
)
acc
}
//Map created
jTreeMap.get(xyz.head).replaceAll{
//hack for scala 2.11.x
new java.util.function.BiFunction[Int, Int, Int]{
def apply(key: Int, value: Int) = if (value < 5) 5 else value
}
}
//value edited
It is not possible to update an immutable object, you can only create a new immutable object from the old one. So the code needs to create a new TreeMap from the original one with different values as necessary.
The code looks like this:
val newMap = a.map{
case (k, v) if k == xyz(0) =>
k -> v.map {
case (k2, v2) if k2 < 50 =>
k2 -> 5
case (k2, v2) =>
k2 -> v2
}
case (k, v) =>
k -> v
}
This breaks down to an outer map that looks for matching keys in the outer TreeMap, and an inner map that looks for matching keys in the inner TreeMap. Pattern matching (case) is used to implement the match tests, and also to extract the keys and values.
Each map has one case that selects the values to be modified, and a second case that leaves other values unchanged. The first case returns the original key with a modified value while the second case just returns the original values (k -> v).
Also note that var applies to a variable, not the contents of a variable. It indicates whether the variable can be updated to refer to a different object, but says nothing about whether the object that the variable refers to can be updated. var is rarely used in Scala because it goes against a clean functional design.

Scala populate map with random values

I need to create a test for various collections based on Map and HashMap.
I have two functions that create test data, e.g.:
def f1: String = { ... )
def f2: String = { ... }
these functions create random data every time they are called.
My map is:
val m:Map[String,String] = ...
what I try to accomplish is construct an immutable map with 10000 random items that were generated by calling f1/f2. so protocode would be:
for 1 to 10000
add-key-value-to-map (key = f1(), value = f2() )
end for
how can I accomplish this in scala, without destroying and rebuilding the list 10000 times?
EDIT:
Since it wasn't clear in the original post above, I am trying to run this with various types of maps (Map, HashMap, TreeMap).
List.fill(10000)((f1, f2)).toMap
You can use List.fill to create a List of couple (String, String) and then call .toMap on it:
scala> def f1 = util.Random.alphanumeric take 5 mkString
f1: String
scala> def f2 = util.Random.alphanumeric take 5 mkString
f2: String
scala> val m = List.fill(5)(f1 -> f2).toMap
m: scala.collection.immutable.Map[String,String] =
Map(T7hD8 -> BpAa1, uVpno -> 6sMjc, wdaRP -> XSC1V, ZGlC0 -> aTwBo, SjfOr -> hdzIN)
Alternatively you could use Map/HashMap/TreeMap's .apply function:
scala> val m = collection.immutable.TreeMap(List.fill(5)(f1 -> f2) : _*)
m: scala.collection.immutable.TreeMap[String,String] =
Map(3cieU -> iy0KV, 8oUb1 -> YY6NC, 95ol4 -> Sf9qp, GhXWX -> 8U8wt, ZD8Px -> STMOC)
val m = (1 to 10000).foldLeft(Map.empty[String,String]) { (m, _) => m + (f1 -> f2) }
Using tabulate as follows,
Seq.tabulate(10000)(_ => f1 -> f2).toMap
It proves unclear whether the random key generator function may duplicate some keys, in which case 10000 iterations would not suffice to produce a map of such size.
An intuitive approach,
(1 to 10000).map(_ => f1 -> f2).toMap
Using a recursive function instead of generating a range to iterate over, (although numerous intermediate Maps are created),
def g(i: Int): Map[String,String] = {
if (i<=0)
Map()
else
Map(f1 -> f2) ++ g(i-1)
}

How to use Reduce on Scala

I am using scala to implement an algorithm. I have a case where I need to implement such scenario:
test = Map(t -> List((t,2)), B -> List((B,3), (B,1)), D -> List((D,1)))
I need to some the second member of every common tuples.
The desired result :
Map((t,2),(B,4),(D,1))
val resReduce = test.foldLeft(Map.empty[String, List[Map.empty[String, Int]]){(count, tup) => count + (tup -> (count.getOrElse(tup, 0) + 1))
I am trying to use "Reduce", I have to go through every group I did and sum their second member. Any idea how to do that.
If you know that all lists are nonempty and start with the same key (e.g. they were produced by groupBy), then you can just
test.mapValues(_.map(_._2).sum).toMap
Alternatively, you might want an intermediate step that allows you to perform error-checking:
test.map{ case(k,xs) =>
val v = {
if (xs.exists(_._1 != k)) ??? // Handle key-mismatch case
else xs.reduceOption((l,r) => l.copy(_2 = l._2 + r._2))
}
v.getOrElse(??? /* Handle empty-list case */)
}
You could do something like this:
test collect{
case (key, many) => (key, many.map(_._2).sum)
}
wherein you do not have to assume that the list has any members. However, if you want to exclude empty lists, add a guard
case (key, many) if many.nonEmpty =>
like that.
scala> val test = Map("t" -> List(("t",2)), "B" -> List(("B",3), ("B",1)), "D" -> List(("D",1)))
test: scala.collection.immutable.Map[String,List[(String, Int)]] = Map(t -> List((t,2)), B -> List((B,3), (B,1)), D -> List((D,1)))
scala> test.map{case (k,v) => (k, v.map(t => t._2).sum)}
res32: scala.collection.immutable.Map[String,Int] = Map(t -> 2, B -> 4, D -> 1)
Yet another approach, in essence quite similar to what has already been suggested,
implicit class mapAcc(val m: Map[String,List[(String,Int)]]) extends AnyVal {
def mapCount() = for ( (k,v) <- m ) yield { (k,v.map {_._2}.sum) }
}
Then for a given
val test = Map("t" -> List(("t",2)), "B" -> List(("B",3), ("B",1)), "D" -> List(("D",1)))
a call
test.mapCount()
delivers
Map(t -> 2, B -> 4, D -> 1)

Scala: Grouping list of tuples

I need to group list of tuples in some unique way.
For example, if I have
val l = List((1,2,3),(4,2,5),(2,3,3),(10,3,2))
Then I should group the list with second value and map with the set of first value
So the result should be
Map(2 -> Set(1,4), 3 -> Set(2,10))
By so far, I came up with this
l groupBy { p => p._2 } mapValues { v => (v map { vv => vv._1 }).toSet }
This works, but I believe there should be a much more efficient way...
This is similar to this question. Basically, as #serejja said, your approach is correct and also the most concise one. You could use collection.breakOut as builder factory argument to the last map and thereby save the additional iteration to get the Set type:
l.groupBy(_._2).mapValues(_.map(_._1)(collection.breakOut): Set[Int])
You shouldn't probably go beyond this, unless you really need to squeeze the performance.
Otherwise, this is how a general toMultiMap function could look like which allows you to control the values collection type:
import collection.generic.CanBuildFrom
import collection.mutable
def toMultiMap[A, K, V, Values](xs: TraversableOnce[A])
(key: A => K)(value: A => V)
(implicit cbfv: CanBuildFrom[Nothing, V, Values]): Map[K, Values] = {
val b = mutable.Map.empty[K, mutable.Builder[V, Values]]
xs.foreach { elem =>
b.getOrElseUpdate(key(elem), cbfv()) += value(elem)
}
b.map { case (k, vb) => (k, vb.result()) } (collection.breakOut)
}
What it does is, it uses a mutable Map during building stage, and values gathered in a mutable Builder first (the builder is provided by the CanBuildFrom instance). After the iteration over all input elements has completed, that mutable map of builder values is converted into an immutable map of the values collection type (again using the collection.breakOut trick to get the desired output collection straight away).
Ex:
val l = List((1,2,3),(4,2,5),(2,3,3),(10,3,2))
val v = toMultiMap(l)(_._2)(_._1) // uses Vector for values
val s: Map[Int, Set[Int] = toMultiMap(l)(_._2)(_._1) // uses Set for values
So your annotated result type directs the type inference of the values type. If you do not annotate the result, Scala will pick Vector as default collection type.

Scala iterate over map and turn singleton List into just the singleton

I am trying to extract a value of type List[T] to just T in a Map. So for instance:
val c = Map(1->List(1), 2-> List(2), 3->List(3));
would turn into
Map(1->1,2->2,3->3);
Here is what I have written so far:
val Some(values) = request.body.asFormUrlEncoded.foreach {
case (key,value) =>
Map(key->value.head);
};
and here is the error I am receiving:
constructor cannot be instantiated to expected type; found : (T1, T2) required: scala.collection.immutable.Map[String,Seq[String]]
EDIT: This is ocurring wrt to this line:
case (key,value) =>
EDIT2:
request.body.asFormUrlEncoded example output
Some(Map(test -> List(324)))
Some(Map(SpO2 -> List(456), ETCO2 -> List(123)))
Are you sure that you will always have exactly one element in the list? If so, you should do this, which is clear, and has the benefit that it will throw an error if you get a bad list (doesn't have exactly one element) by accident.
c.map { case (k, List(v)) => k -> v }
// Map(1 -> 1, 2 -> 2, 3 -> 3)
If your lists can have more than one element, and you just want the first, you can do this (which will error on empty lists):
val d = Map(1 -> List(1), 2 -> List(2,4,6), 3 -> List(3))
d.map { case (k, List(v, _*)) => k -> v }
// Map(1 -> 1, 2 -> 2, 3 -> 3)
If your lists may not have exactly one element, and you want to ignore any non-singleton lists instead of throwing errors, use collect instead of map:
val e = Map(1 -> List(1), 2 -> List(2,4,6), 3 -> List(3), 4 -> List())
e.collect { case (k, List(v)) => k -> v }
// Map(1 -> 1, 3 -> 3)
As for your code:
val Some(values) = request.body.asFormUrlEncoded.foreach {
case (key,value) =>
Map(key->value.head);
};
This doesn't really make any sense.
First off, foreach doesn't return anything, so assigning its result to a variable will never work. You probably want this to be a map instead, so that it returns a collection.
Second, your use of Some makes it seem like you don't understand Options, so you might want to read up on that.
Third, if you want the result to be a Map (a collection of pairs), then you'll just want to return the pair, key->value.head, and not a Map.
Fourth, if you're getting errors matching on case (key,value), then probably asFormUrlEncoded doesn't actually return a collection of pairs. You should see what its type actually is.
Lastly, the semicolons are unnecessary. You should remove them.
EDIT based on your comment:
Since request.body.asFormUrlEncoded actually returns things like Some(Map("test" -> List(324))), here is how your code should look.
If asFormUrlEncoded might return None, and you don't have any way of handling that, then you should guard against it:
val a = Some(Map("test" -> List(324)))
val value = a match {
case Some(m) => m.collect { case (k, List(v)) => k -> v }
case None => sys.error("expected something, got nothing")
}
If you're sure that asFormUrlEncoded will already return Some, then you can just do this:
val a = Some(Map("test" -> List(324)))
val Some(value) = a.map(_.collect { case (k, List(v)) => k -> v })