Scala: Iterate over mutable map of maps - scala

I have defined a mutable map of maps
import scala.collection.mutable.Map
val default = Map.empty[String, Int].withDefaultValue(0)
val count = Map.empty[Any, Map[String, Int]].withDefaultValue(default)
which I populate/update as in
count("furniture")("table") += 1
count("furniture")("chair") = 6
count("appliance")("dishwasher") = 1
How can I iterate over all items in count? And why does count.keys return an empty Set()?

With default, does not create new Map when no value exists in collection, it just returns default value on such requests, and other changes are done on this default value.
count("furniture")("table") += 1
count("furniture")("chair") = 6
count("appliance")("dishwasher") = 1
count("banana") // will return Map with "table", "chair" & "dishwasher"
is equivalent
default("table") += 1
default("chair") = 6
default("dishwasher") = 1
And since you return this default value on any key, this default map will be returned on every call.
Your code will work like this.
count("furniture") = Map.empty[String, Int].withDefaultValue(0)
count("appliance") = Map.empty[String, Int].withDefaultValue(0)
count("furniture")("table") += 1
count("furniture")("chair") = 6
count("appliance")("dishwasher") = 1

There are several problems with your approach:
Issue #1:
val default = Map.empty[String,Int].withDefaultValue(0)
defines a value default. There is only one instance of this value and it can not be changed, since you defined as a val.
That means that your count map has a default value which is always the same instance of an empty map. Since count is empty, count("furniture") or count("appliance") is exactly the same as just default.
Issue #2:
withDefaultValue does not add entries to a map it just returns a default for undefined keys.
See #mavarazys answer

Related

Gatling Feeder running out of values

I have an array that i want to use for 2 feeders. I was expecting each feeder will be able to use all the values in the array. But seems like the values run out
val baseArray = Array ( Map("transactionId" -> "q-1"),
Map("transactionId" -> "q-2"),
Map("transactionId" -> "q-3"))
val feeder_getA = baseArray.clone.queue
val scn_getInsuredOrPrincipals = scenario("getInsuredOrPrincipals")
.feed(feeder_getA)
.exec(http("request_getA").get("/getA/${transactionId}"))
val feeder_getB = baseArray.clone.queue
val scn_getInsuredOrPrincipals = scenario("getInsuredOrPrincipals")
.feed(feeder_getB)
.exec(http("request_getB").get("/getB/${transactionId}"))
setUp(
scn_getInsuredOrPrincipals.inject(
atOnceUsers(3), // 2
rampUsers(3) over (5 seconds)
),
scn_getInsuredOrPrincipal.inject(
atOnceUsers(3), // 2
rampUsers(3) over (5 seconds)
)
)
I get an error saying feeder is now empty after 3 values are consumed... i was assuming feeder_getA and feeder_getB would each get 3 values so each scenario would get equal number of values. That doesnt seem like its happening. Almot as if the clone isnt working
The issue is that your feeders are defined using the queue strategy, which runs through the elements and then fails if no more are available:
val feeder_getA = baseArray.clone.queue
You need to use the circular strategy, which goes back to the beginning:
val feeder_getA = baseArray.clone.circular
For more information see the docs.

Scala how to group a map and then subgroup and transform values

I have an object like this:
case class MyObject(x : Int,y : String,...) {
val buckets = 3
def bucket = x % buckets // returns a number between 0 and |buckets|
}
(x is an arbitrary number)
for example assume "buckets = 3" and we have many objects
MyObject(x = 0, y = "Something", ...)
MyObject(x = 1, y = "Something else", ...)
....
...
Using "groupBy" I collect "MYObjects" using the x % buckets, so it will be like:
val objects : Seq[MyObject] = ...
val groupedObjects : Map[Int: Seq[MyObjects]] = objects.groupBy(obj => x.bucket)
now I want to transform each value and also regroup to sublists of the different type
so lets say for each item in group = 1 , I want to nest under an additional layer and store a different calculated value:
so lets say if bucket 0 after the initial grouping looked like:
bucket[0] = [obj1,obj2,...,objn]
I want to be able to transform bucket "0" to contain another nested grouping:
bucket[0] = Map(sub_bucket_0 -> [transformed(objects)...], sub_bucket_1 -> [transformed(object)...),....]
meaning that eventually I have a data structure with the type:
Map[Int,Map[Sub_bucket_type,Seq[TransformedObject_type]]]
I think what you're looking for is mapValues() which will modify the Map's value elements to new values and/or types.
groupedObjects.mapValues(_.groupBy(/*returns new key type/value*/))
.mapValues(_.mapValues(_.map(/*transform MyObject elements*/)))

Spark - not getting a int from a value of a map in scala

I have a object that is defined as
pLinkGroupsByP: Map[String, Set[(String, Int)]]
I am trying to get Int in the object to be returned if it exists otherwise return 0 thus the .getOrElse(0)
val result:Int =
Try{
pLinkGroupsByP(Doc.productType.id)
.contains(Doc.Group.id,container.containerDoc.GroupOrder)
(Doc.Group.id,container.containerDoc.GroupOrder)._2
}.getOrElse(0)
(Doc.productType.id) is to access the key
(Doc.Group.id) is to access the value 1st part of the value
(container.containerDoc.GroupOrder) is to access the 2nd part of value which is what I need to be assigned to result.
However, I am getting only the first number of the first value in the map no matter what. I just need the number in the value to be assigned back to the result in this try statement.
Sample data is
Map["pro-ucw32, Set[(PRD-1292, 5)]]
I guess you can get the result with simple if else
val result:Int = if(pLinkGroupsByP(Doc.productType.id).contains(Doc.Group.id,container.containerDoc.GroupOrder)) container.containerDoc.GroupOrder else 0
val tupleToMap = pLinkGroupsByP(Doc.productType.id).toMap
tupleToMap(relatedDoc.lkGroup.id).GetorElse(0)
The tuple to map converts value is assigned a tuple to map,
relatedDoc.linkGroup.id is a key being mapped and retunred if its a number greater than 1 it will be returned and then a 0

Variable not updated

This is perhaps very basic but I am certainly missing something here.
It is all within a method, where I increment/alter a variable within a child/sub scope (it could be within a if block, or like here, within a map)
However, the result is unchanged variable. e.g. here, sum remains zero after the map. whereas, it should come up to 3L.
What am I missing ?
val resultsMap = scala.collection.mutable.Map.empty[String, Long]
resultsMap("0001") = 0L
resultsMap("0003") = 2L
resultsMap("0007") = 1L
var sum = 0L
resultsMap.mapValues(x => {sum = sum + x})
// I first wrote this, but then got worried and wrote more explicit version too, same behaviour
// resultMap.mapValues(sum+=_)
println("total of counts for txn ="+sum) // sum still 0
-- Update
I have similar behaviour where a loop is not updating the variable outside the loop. looking for text on variable scoping, but not found the golden source yet. all help is appreciated.
var cnt : Int = 0
rdd.foreach(article => {
if (<something>) {
println(<something>) // being printed
cnt += 1
println("counter is now "+cnt) // printed correctly
}
})
You should proceed like this:
val sum = resultsMap.values.reduce(_+_)
You just get your values and then add them up with reduce.
EDIT:
The reason sum stays unchanged is that mapValues produces a view, which means (among other things) the new map won't be computed unless the resulting view is acted upon, so in this case - the code block updating sum is simply never executed.
To see this - you can "force" the view to "materialize" (compute the new map) and see that sum is updated, as expected:
var sum = 0L
resultsMap.mapValues(x => {sum = sum + x}).view.force
println("SUM: " + sum) // prints 3
See related discussion here: Scala: Why mapValues produces a view and is there any stable alternatives?

Scala: How to create a map over a collection from a set of keys?

Say I have a set of people Set[People]. Each person has an age. I want to create a function, which creates a Map[Int, Seq[People]] where for each age from, say, 0 to 100, there would be a sequence of people of that age or an empty sequence if there were no people of that age in the original collection.
I.e. I'm doing something along the lines
Set[People].groupBy(_.age)
where the output was
Map[Int, Seq[People]](0 -> Seq[John,Mary], 1-> Seq[People](), 2 -> Seq[People](Bill)...
groupBy of course omits all those ages for which there are no people. How should I implement this?
Configure a default value for your map:
val grouped = people.groupBy(_.age).withDefaultValue(Set())
if you need the values to be sequences you can map them
val grouped = people.groupBy(_.age).mapValues(_.toSeq).withDefaultValue(Seq())
Remember than, as the documentation puts it:
Note: `get`, `contains`, `iterator`, `keys`, etc are not affected by `withDefault`.
Since you've got map with not empty sequences corresponding to ages, you can fill the rest with empty collections:
val fullMap = (0 to 100).map (index => index -> map.getOrElse(index, None)).toMap