Update mutable HashMap value which is a mutable collection - scala

I'm have a map that looks like this: Map[ A -> Collection[B]]. This map gets updated in a loop - the special thing is however, that updates mostly just mean adding an element B to the Collection[B] (for some key A).
I am trying to find out if I can get some speedup by changing the type of my Collection from List[ ] to ListBuffer[ ].
Up to now my code looked like this (simplified):
var incoming = new HashMap[A, List[B]() {
override def default(a: A) = List()
}
..
for(b < someCollectionOfBs){
..
incoming(b.getA) = b :: incoming(b.getA)
..
}
This works fine. Now, I changed the type of the map so it looks like this:
var incoming = new collection.mutable.HashMap[A, ListBuffer[B]() {
override def default(a: A) = collection.mutable.ListBuffer()
}
..
for(b < someCollectionOfBs){
..
incoming(b.getA) += b
..
}
Note the change in how the element B is added to the collection in the 2nd example (no more immutable List, hence we do not need to create and assign new collection...).
But. This does not work: incoming(X) += .. does not update the value of the map for X, actually it does not change anything.
What am I missing here? I thought that I should be able to update the values of a mutable HashMap... So, if my values are mutable collections, why can't I just add elements to those?

The default is returned when the key is not found, but it does not update the map with the default value. You can use getOrElseUpdate for that.
incoming.getOrElseUpdate(b.getA, ListBuffer()) += b
That should do what you want.
Additional note:
If you're concerned about performance, I don't think replacing List with ListBuffer will buy you much, because you are prepending to a List and that should be very fast. ListBuffer is handy when you want to append to a list. You should look at using java.util.HashMap and see if that helps.

Related

What is the correct way to make a list in Scala?

With a background from object-oriented programming I am not able to understand how to make immutable lists in Scala.
Example; I want to make a list of 10 random people:
object MyApplication extends App {
val numberOfPersons = 10 : Int
val listOfPersons = makeListOfPersons(numberOfPersons) : List[Person]
def makeListOfPersons( numberOfPersons : Int ) : List[Person] = {
// TODO: return a immutable list of 10 persons
}
}
class Person {
/**
Generic content,
like age and name.
* */
}
What is the "correct" way of making an immutable list in Scala?
If you know what collection type you want, you may be able to use the tabulate method on that type:
List.tabulate(10)(makePerson)
In this case makePerson is a function that takes an Int and returns the Person object for that Int.
If you don't care about the collection type, you can call map on the range 1 to 10 like this:
(1 to 10).map(makePerson)
If you don't need to use the Int parameter, you can do this:
List.tabulate(10)(_ => makeRandomPerson())
In this particular case,
List.fill(numberOfPersons){ codeThatCreatesASinglePerson }
seems most appropriate.
In most other cases: Nil creates an empty list, x :: y prepends an element x to list y.
If you want to append to list, instead of prepending to it, then you can take a collection.mutable.ListBuffer, append to it all the elements that you want to have in the list, and then call toList when you're done... or just use the built-in factory methods that do exactly that.
As the default List in Scala is immutable, the right way to add an element is to return a new list with the new element plus the older elements.
As a matter of fact, List has two methods, among others:
+:
++
The first one takes an element, add it as the first element and the rest of the list as it's tail and then returns the resulting list.
The other one takes another "collection" as parameter and adds it to the first list at the start.
List has another methods for adding the new element as the last one.
In Scala, these operations are permitted but take into consideration that always a new instance will be retrieved with the requested modifications as all objects are immutable by default.
As for your code goes, you could try with something like this:
object MyApplication extends App {
val numberOfPersons: Int = 10
val listOfPersons: List[Person] = makeListOfPersons(numberOfPersons)
def makeListOfPersons( numberOfPersons : Int ) : List[Person] = {
(1 to numberOfPersons).foldLeft(List.empty[Person]){ (accum, elem) =>
new Person() :: accum
}
}
}
(1 to numberOfPersons) creates a range, which could be seen as a List of ints, which will be traversed by foldLeft. This method will iterate through that list, and receives a seed, in this case an empty list of Person. Then, for every element in the int's list, a new Person is created and add to the list, returned as is the last expression and used the accumulator for the next iteration. Finally, a list of ten instances of Person is retrieved.
There are 5 ways to create List in scala:
Lisp style:
val list = 1::2::3::Nil
this style can also be thought of as a Haskell or functional programming (FP) style.
Java Style:
val list = List(1,2,3)
Scala List with range method
List.range(1, 10)
Create scala List with fill
List.fill(3)(5)
Scala List with tabulate
List.tabulate(5)(n => n * n)
element of the list are created according to the function we supply.
for more info please read this :
Preferred way to create a Scala list

Get and remove an arbitrary element from a mutable HashSet in Scala

How do you get and remove an arbitrary element from a mutable HashSet in Scala (similar to python set pop method)? Also what would be the performance of the api?
For extracting and removing elements in undefined order from a mutable hash set, try a combination of head and -= (remove):
import scala.collection.mutable.HashSet
def pop[A](hs: HashSet[A]): A = {
val a = hs.head
hs -= a
a
}
val example = HashSet.empty[Int] ++ (0 to 9)
while (!example.isEmpty) {
val p = pop(example)
println("Pop element: " + p)
}
I'm not sure whether it's caching any keys or hashes internally, but afaik it's either just O(1) or amortized O(1).
#Andrey's answer above should suffice to answer your question as he said
you can use .head function to pop an element.
Here are some more details.
Sets are one of the collection libraries of Scala which provide both mutable and immutable methods. If you want specific element to be removed from Sets then there is - and + methods. - method is used for removing an element from a Set and a new Set is returned. Similarly + method is used for adding an element in a Set which also returns a new Set with the element added. So you can write pop and set functions that will remove and add specific elements.
def popFromSet[T](set: Set[T], stringToPop: T) = {
set - stringToPop
}
def setToSet[T](set: Set[T], stringToPop: T) = {
set + stringToPop
}
And you can use them as
popFromSet(stringSet, "second")
setToSet(popFromSet(stringSet, "second"), "second2")
popFromSet(intSet, 2)
....
List way
You can do the above Set to List
If you have a HashSet as
Set("first", "second", "third")
You can get the second element popped out by doing
val hashList = hashSet.toList
hashList(hashList.indexOf("second"))
and if you want to remove the second element from the HashSet then
hashSet - hashList(hashList.indexOf("second"))
I hope the answer is helpful

Filtering and modifying immutable sequence in loop and have changes effective in subsequent filter calls

I guess I kinda murdered the title but I could not express it another way.
I have a trait like this:
trait Flaggable[T <: Flaggable[T]] { self : T =>
def setFlag(key: String, value: Boolean): T
def getFlag(key:String): Boolean
}
This trait itself is not that important, but main thing here is class implementing it should be immutable as setFlag returns a new instance. Example class extending this trait:
class ExampleData(val flags: Map[String, Boolean] = Map())
extends Flaggable[ExampleData] {
def setFlag(key: String, value: Boolean): ExampleData =
new ExampleData(flags + (key->value))
def getFlag(key:String): Boolean = flags(key)
}
While iterating over collection I set flags on elements and I want those flags to be effective in subsequent iterations. Something like
val seq: Seq[ExampleData] = ???
seq.view.filter(el => !el.getFlag("visited")).foreach { el =>
// do things that may set flag visited to true in elements
// further in the seq, if that happens I do want those
// elements to be filtered
}
Now AFAIK, one option is to make seq mutable and assign new instances returned from setFlag to seq. Another option is to make whole flaggable thing mutable and modify instances in place in collection. Do I have any other option without making either of these (class and collection) mutable? I do not even know how can I modify and filter at the same time in that case.
I guess I should explain my situation more. Specifically, I am trying to implement dbscan clustering algorithm. I have a function that can return distance between two data points. For each data point, I need to get data points that is closer than an epsilon to that data point and mark those visited. And I do not want to process data points that is marked visited again. For example, for data point with index 0, the index list of data points closer than epsilon might be [2, 4, 5]. In this case I want to flag those data points as visited and skip over them without processing.
Just use map instead of foreach and replace the order of the functions:
seq.view.map { el =>
// do things that may set flag visited to true and return the new
// or e1 if no change needed.
}.filter(el => !el.getFlag("visited"))
Update:
Since the filter and the update related to each other, use mutable collection. I prefer that than mutable data objects, since it can be limit only to that scope. (e.g. use seq.to[ListBuffer]). after you done, all the mutations gone.... This allow keep the mutable code locally.
Nevertheless, depends on your algorithm, there may be a better collection for that, like Zipper.
I think you could extract a function to handle what is done inside your foreach with a signature like:
def transform(in: ExampleData): ExampleData
with that you could use a for comprehension:
for {
elem <- seq if !elem.getFlag("visited")
result = transform(elem) if result.getFlag("Foo")
} yield result
If you have multiple operations you can just append:
for {
elem <- seq if !elem.getFlag("visited")
r1 = transform(elem) if r1.getFlag("Foo")
r2 = transform2(r1) if r2.getFlag("Bar")
} yield r2
The result would be a new Seq of new ExampleData according to the transformations and filters applied.
In general, if you want to both filter and process elements you would usually use the collect function and possibly chain them:
seq.collect {
case elem if !elem.getFlag("visited") => transform(elem)
}.collect {
case elem if elem.getFlag("Foo") => transform2(elem)
}.filter(_.getFlag("Bar")

Scala: mutable HashMap does not update inside for loop

I have a var permutedTables = HashMap[List[Int], List[String, String]] defined globally. I first populate the Hashmap with the keys in a method, which works.
print(permutedTables) :
Map(List(1,2,3,4) -> List(),
List(2,4,5,6) -> List(), etc...)
The problem occurs when I want to update the values (empty lists) of the HashMap inside a for loop (inside a second method). In other words, I want to add tuples (String, String) in the List() for each key.
for(pi_k <- permutedTables.keySet){
var mask = emptyMask;
mask = pi_k.foldLeft(mask)((s, i) => s.updated(i, '1'))
val maskB = Integer.parseInt(mask,2)
val permutedFP = (intFP & maskB).toBinaryString
// attempt 1 :
// permutedTables(pi_k) :+ (url, permutedFP)
// attempt 2 :
// permutedTables.update(pi_k, permutedTables(pi_k):::List((url, permutedFP)))
}
The values do not update. I still have empty lists as values. I don't understand what is wrong with my code.
EDIT 1: When I call print(permutedTables) after any of the two attempts (inside the loop), the value seem updated, but when I call it outside of the loop, the Lists are empty
EDIT 2: The second attempt in my code seems to work now(!). But why does first not work ?
The second attempt in my code seems to work now(!). But why does first not work ?
Because what you do in the first case is get a list from permutedTables, add an element and throw away the result without storing it back. It would work if you mutated the value, but a List is immutable. With List, you need
permutedTables += pi_k -> permutedTables(pi_k) :+ (url, permutedFP)
Or, as you saw, update.
You can use e.g. ArrayBuffer or ListBuffer as your value type instead (note that you need :+= instead of :+ to mutate them), and convert to your desired type at the end. This is going to be rather more efficient than appending to the end of the list, unless the lists are quite small!
Finally, note that you generally want either var or a mutable type, not both at the same time.

adding an object to list in scala

I am trying to add and object to a list not just a number so when you reply with an example if you could use an object like my used car example or a fruit or something
I've been looking every where and all the examples i see are just adding numbers to a list.
Im trying to convert some java code into scala
the java code im having trouble converting is
ArrayList usedCarList = new ArrayList();
UsedCars usedCar = new UsedCars();
usedCarList.add(usedCar);
Now i've looked at a few examples but they dont seem to work once i start trying to use an object ie
var b = List[Int]();
b ::= 1;
b ::= 2;
b ::= 3;
I've tried a few things 1 is listed below.
var usedCarList = List();
def addCar (usedCarList: List[UsedCars]){
var usedCar = new UsedCars();
series of set operations on the usedCar
usedCar :: usedCarList;
println(usedCarList.length);
}
when i check the size of the list its always empty
There are mutable (such as scala.collection.mutable.MutableList) and immutable lists (scala.collection.immutable.List). What you're using are immutable lists, so by just calling :: on an element actually returns a new instance with the added element, but doesn't change the underlying value. You'd have to either use a var with an immutable list, or use a mutable one like this:
scala> import scala.collection._
import scala.collection._
scala> val list = mutable.MutableList[UsedCars]()
list: scala.collection.mutable.MutableList[UsedCars] = MutableList()
scala> list += new UsedCars()
res0: list.type = MutableList(UsedCars#4bfa79c8)
scala> list.size
res1: Int = 1
See su-'s answer for reassigning the reference with the immutable lists.
There's a fundamental distinction between Scala's List and Java's ArrayList: Scala's List is immutable.
Not simply read only -- a read only collection in Java may still be changed by whoever created it. In Scala, a List cannot be changed by anyone.
Now, let's reconcile that with the example you showed that "works": b ::= 1. That example is equivalent to b = 1 :: b, so it isn't changing the list. Instead, it is creating a new list, and assigning it to b. That may sound inefficient, but it is actually quite fast because List is a persistent data structure (look that up for more information).
The most obvious answer to your question, therefore, is to use a different kind of data structure. Scala's closest equivalent to ArrayList is the ArrayBuffer.
However, there's another possibility that may be pursued. Instead of changing the list and returning nothing, return a new list. That may well require other changes to the structure of the code, and since there's no detail about it, I won't speculate on what they might be.