I am trying to emulate a defaultdict(list) in Scala (I've already declared scala.collection.mutable.HashMap).
Right now I am trying
val d = new HashMap[Int,ListBuffer[Int]](){ override def default(key:Int) = ListBuffer[Int]() }
And then to append to a list I try something like:
d(integerKey) += integerValToInsertIntoList
But nothing seems to work and d acts like it's always empty?
Here is the proper way of implementing this for mutable Maps:
val d = new HashMap[Int,ListBuffer[Int]]() {
override def apply(key: Int) = super.getOrElseUpdate(key, ListBuffer())
}
d(4) append 4
d(5) append 5
scala> d(4)
res1: scala.collection.mutable.ListBuffer[Int] = ListBuffer(4)
scala> d(5)
res2: scala.collection.mutable.ListBuffer[Int] = ListBuffer(5)
Note
Using withDefaultValue does not work as expected for mutable Maps. It will reuse the same "default" ListBuffer() for all new entries.
val d = new HashMap[Int,ListBuffer[Int]]().withDefaultValue(ListBuffer())
d(4) append 4
d(5) append 5
In the REPL we see that d(4) (same for d(5)) will hold both new added entries:
scala> d(4)
res1: scala.collection.mutable.ListBuffer[Int] = ListBuffer(4, 5)
scala> d(5)
res2: scala.collection.mutable.ListBuffer[Int] = ListBuffer(4, 5)
Related
I have a list (size of the list is variable):
val ids = List(7, 8, 9)
and would like to get the following map:
val result= Map("foo:7:bar" -> "val1",
"foo:8:bar" -> "val1",
"foo:9:bar" -> "val1")
everything in the map is hard-coded except ids and value is the same for everyone, but map has to be mutable, I'd like to update one of its values later:
result("foo:8:bar") = "val2"
val result= Map("foo:7:bar" -> "val1",
"foo:8:bar" -> "val2",
"foo:9:bar" -> "val1")
You can do that like this: First map over the list to produce a list of tuples, then call toMap on the result which will make an immutable Map out of the tuples:
val m = ids.map(id => ("foo:" + id + ":bar", "val1")).toMap
Then convert the immutable Map to a mutable Map, for example like explained here:
val mm = collection.mutable.Map(m.toSeq: _*)
edit - the intermediate immutable Map is not necessary as the commenters noticed, you can also do this:
val mm = collection.mutable.Map(ids.map(id => ("foo:" + id + ":bar", "val1")): _*)
Try this:
import scala.collection.mutable
val ids = List(7, 8, 9)
val m = mutable.Map[String, String]()
ids.foreach { id => m.update(s"foo:$id:bar", "val1") }
scala> m
Map(foo:9:bar -> val1, foo:7:bar -> val1, foo:8:bar -> val1)
You, don't need to create any intermediate objects, that map does.
Why not use foldLeft?
ids.foldLeft(mutable.Map.empty[String, String]) { (m, i) =>
m += (s"foo:$i:bar" -> "val1")
}
Try Like this:
scala> import scala.collection.mutable
scala> val result = mutable.Map[String, String]()
result: scala.collection.mutable.Map[String,String] = Map()
scala> val ids = List(7, 8, 9)
ids: List[Int] = List(7, 8, 9)
scala> for(x <- ids){
| result("foo:%d:bar".format(x)) = "val1"
| }
scala> result
res3: scala.collection.mutable.Map[String,String] = Map(foo:9:bar -> val1, foo:7:bar -> val1, foo:8:bar -> val1)
Lets take an example where store is my class.
class store(val a:Int) { }
In the code i want to create a list of store.
val list : List[store] = new List[store]()
How can i add a store in the same list?
First off, it's usually a good idea to capitalize your class names.
scala> class Store(val a:Int) { }
defined class Store
scala> val list : List[Store] = List.empty
list: List[Store] = List()
scala> val newList = new Store(4) :: list
newList: List[Store] = List(Store#243306de)
Your list is, by default, immutable so you'll have a new list every time an element is added.
scala> val newerList = new Store(71) :: newList
newerList: List[Store] = List(Store#30f57af0, Store#243306de)
ADDENDUM
If you need a mutable list (not usually recommended) you could try the following.
scala> import scala.collection.mutable.MutableList
import scala.collection.mutable.MutableList
scala> val myList: MutableList[Store] = MutableList.empty
myList: scala.collection.mutable.MutableList[Store] = MutableList()
scala> myList += new Store(56)
res322: myList.type = MutableList(Store#6421614e)
scala> myList += new Store(29)
res323: myList.type = MutableList(Store#6421614e, Store#85b26)
scala> myList += new Store(11)
res324: myList.type = MutableList(Store#6421614e, Store#85b26, Store#5d2f7883)
Mutable variables are considered poor style and an impediment to proper Functional Programming.
To add element to the beginning of the list use :: :
val l = List(1, 2, 3)
val l1 = 5 :: l // List(5, 1, 2, 3)
or
val l1 = l.::(5)
To add element to the end of the list use :+ :
val l2 = l :+ 5 // List(1, 2, 3, 5)
So, to add store object to the end of the list (though it is not efficient), write this:
val s = new Store(1)
val newList = list :+ s // list is immutable
I have to return the index of the first element in the list that contains the ’?’ character.
Why does it come up as -1 when it should be 8. Does indexOf not work on characters? Should I use indexWhere or will that have the same result?
scala> val lst = List("question?mark")
lst: List[String] = List(question?mark)
scala> lst.indexOf('?')
res2: Int = -1
When I make the val a string it works correctly
Try lst.indexWhere(_.contains("?"))
indexOf "Finds index of first occurrence of some value in this list." link
Therefore it finds string that equals "?" in the list.
.indexOf works on String
scala> "where is ?".indexOf('?')
res5: Int = 9
so try
lst(0).indexOf('?')
for each element of list you can use
scala> val lst = List("question?mark","where?","hello","why?")
lst: List[String] = List(question?mark, where?, hello, why?)
scala> lst.map(_.indexOf('?'))
res4: List[Int] = List(8, 5, -1, 3)
If I have
val incomingIds : List[Int] = ....
val existingIds : List[Int] = //this makes db calls and find existing records (only interested in returning ids)
Now next I want to compare incomingIds with existingIds in a following way
say I have
val incomingIds : List[Int] = List(2,3,4,5)
val existingIds : List[Int] = List(1,2,3,6)
What above sample suggests is that my API should be able to find ids that are subject for deletion (ones that exist in incomingIds but not in existingIds). In this sample existingIds have 1,4,5 but they aren't there in incomingIds means 1,4,5 should go into
val idsForDeletion :List[Int]
and there will be another list call it
val idsForInsertion :List[Int].
so 6 should go into idsForInsertion list.
Is there a simple way to partition lists such a way?
You can filter the items from each list that aren't included in the other one using scala's filterNot function:
val idsForDeletion = existingIds.filterNot(incomingIds.toSet)
val idsForInsertion = incomingIds.filterNot(existingIds.toSet)
you can use diff
def diff(that: collection.Seq[A]): List[A]
It Computes the multiset difference between this list and another sequence.
scala> val incomingIds : List[Int] = List(2,3,4,5)
incomingIds: List[Int] = List(2, 3, 4, 5)
scala> val existingIds : List[Int] = List(1,2,3,6)
existingIds: List[Int] = List(1, 2, 3, 6)
scala> (incomingIds diff existingIds).toSet
res1: scala.collection.immutable.Set[Int] = Set(4, 5)
scala> (existingIds diff incomingIds).toSet
res2: scala.collection.immutable.Set[Int] = Set(1, 6)
I'm would like to create generic (invariant) method in Scala which copies elements from source list to destination list. In Java there is copy method in java.util.Collections (see http://docs.oracle.com/javase/1.4.2/docs/api/java/util/Collections.html#copy%28java.util.List,%20java.util.List%29). I know in Scala List is immutable object so i would like to create and return new list.
I've written the following code:
def copy[T](dest:List[T], src:List[T]):List[T] = {
if(dest.length<src.length) throw new Exception("IndexOutOfBoundsException")
else if(src==Nil) dest
else {
var ret = dest
ret = dest.updated(0, src.first)
var i=1
val f:(T=>Unit) = a => {
if(i<src.length) ret=ret.updated(i, src(i))
i+=1
()
}
dest.foreach(f)
ret
}
}
But I think it could be written better. Could you help me to write better code? Thanks in advance.
EDITED: Maybe I expressed unclear what I want to do. I have two lists (scala.collection.immutable.List), e.g. src (length=x) and dest(length=y>=x). I would like to replace first x elements of dest list with elements from src list.
Do you mean scala.collection.immutable.List? It is immutable. No need to copy them. Immutable means that nothing can change it, so you can use it in different threads.
Generic way of creating collections in scala is builder. You can get one from CanBuildFrom object. Alternatively you can get it from genericBuilder method of collection instance.
scala> val list = List(1, 2, 3)
list: List[Int] = List(1, 2, 3)
scala> val b = list.genericBuilder[Int]
b: scala.collection.mutable.Builder[Int,List[Int]] = ListBuffer()
scala> list.foreach{ b += _ }
scala> val result = b.result // this code is useless. `val result = list` is enough
result: List[Int] = List(1, 2, 3)
If you want to create new collection of different type based on existing collection, you can use collection.breakOut methid like this:
scala> val list = List('a', 'b', 'c')
list: List[Char] = List(a, b, c)
scala> val result: String = list.map{identity}(collection.breakOut)
result: String = abc
Upd
require(src.length <= dest.length, "IndexOutOfBoundsException")
src ++ dest.drop(src.length)
If you want to get an updated list you can use map on your list. Map works by applying a function to each element in the list, and returning updated list.
http://www.brunton-spall.co.uk/post/2011/12/02/map-map-and-flatmap-in-scala/
You are thinking far too procedurally, say what you want not how to do it...
how about:
val src = List(1,2,3)
val dest = src map {x => x}
if you really want to make a function of it
def copy[T](src: List[T]): List[T] = src map {x => x}
in response to OP's update:(which has also been proposed by others)
def copy[T](src: List[T], dest: List[T]): List[T] = src ++ dest.drop(src.length)
You could use:
if(dest.length <= src.length) dest ::: src.drop(dest.length)
else dest.dropRight(dest.length - src.length) //or throw exception...
Maybe you want something like
def copy[T](dest: Seq[T], src: Seq[T]): Seq[T] = {
require(dest.length >= src.length)
src ++ (dest drop src.length)
}
I generalized to Seqs, but it works on Lists, of course
The require method throws IllegalArgumentException if not fulfilled at runtime
Then you need only append the last (y-x) elements of the destination list to to the source list (where x = src.length; y = dest.length)
You do this by dropping x elements from dest and appending the remaining to src.
This is what you get from the REPL
scala> val src = List(1, 2, 3, 4)
src: List[Int] = List(1, 2, 3, 4)
scala> val dst = List(10, 20)
dst: List[Int] = List(10, 20)
scala> val dst2 = List(10, 20, 30, 40, 50, 60)
dst2: List[Int] = List(10, 20, 30, 40, 50, 60)
scala> copy(dst, src)
java.lang.IllegalArgumentException: requirement failed
at scala.Predef$.require(Predef.scala:221)
at .copy(<console>:8)
at .<init>(<console>:11)
at .<clinit>(<console>)
at .<init>(<console>:7)
at .<clinit>(<console>)
<...>
scala> copy(dst2, src)
res1: Seq[Int] = List(1, 2, 3, 4, 50, 60)