Maintaining a Map of case class objects in Scala - scala

I want to maintain a Map of case class objects, such that I can add new instances and look them up by an ID.
My current (very ugly) solution (stripped down):
case class cc(i: Int)
var myccmap: Map[Int, cc] = null
def addcc(thecc: cc): cc = {
if (myccmap == null) {
myccmap = Map(thecc.hashCode, thecc)
}
else {
myccmap = myccmap ++ Map(thecc.hashCode, thecc)
}
thecc
}
And then elsewhere I can use val somecc = addcc(cc(56)), for example, to maintain a Map of my cc objects added with addcc.
This way, I can store the key, which is just the hashCode, with some data in a file, and then when I read the file, I can extract the cc object by looking up the hashCode (which may or may not be present in myccmap).
Is there a better way to do this, ideally without relying on a check for null?

Your code can be simplified, just use a HashSet if you want to use the hash anyway:
import collection.immutable.HashSet
case class Cc(i: Int)
var myccmap: HashSet[Cc] = HashSet.empty
def addcc(thecc: Cc): thecc.type = {
myccmap += thecc
thecc
}
Also, by convention, classes should start with an uppercase letter. I also used a singleton-type as return value of addcc, this way it's clear that this function really just returns its parameter

Depends on whether you really want that key or not. I suppose the key of hashcode is still required because you store the key somewhere. And be noticed, don't use var in most of the cases, instead, use mutable map will help.
case class cc(i: Int)
val myccmap: mutable.Map[Int, cc] = mutable.Map.empty
def addcc(thecc: cc): cc = {
myccmap += (thecc.hashCode -> thecc)
thecc
}

Related

Scala : How to pass a class field into a method

I'm new to Scala and attempting to do some data analysis.
I have a CSV files with a few headers - lets say item no., item type, month, items sold.
I have made an Item class with the fields of the headers.
I split the CSV into a list with each iteration of the list being a row of the CSV file being represented by the Item class.
I am attempting to make a method that will create maps based off of the parameter I send in. For example if I want to group the items sold by month, or by item type. However I am struggling to send the Item.field into a method.
F.e what I am attempting is something like:
makemaps(Item.month);
makemaps(Item.itemtype);
def makemaps(Item.field):
if (item.field==Item.month){}
else (if item.field==Item.itemType){}
However my logic for this appears to be wrong. Any ideas?
def makeMap[T](items: Iterable[Item])(extractKey: Item => T): Map[T, Iterable[Item]] =
items.groupBy(extractKey)
So given this example Item class:
case class Item(month: String, itemType: String, quantity: Int, description: String)
You could have (I believe the type ascriptions are mandatory):
val byMonth = makeMap[String](items)(_.month)
val byType = makeMap[String](items)(_.itemType)
val byQuantity = makeMap[Int](items)(_.quantity)
val byDescription = makeMap[String](items)(_.description)
Note that _.month, for instance, creates a function taking an Item which results in the String contained in the month field (simplifying a little).
You could, if so inclined, save the functions used for extracting keys in the companion object:
object Item {
val month: Item => String = _.month
val itemType: Item => String = _.itemType
val quantity: Item => Int = _.quantity
val description: Item => String = _.description
// Allows us to determine if using a predefined extractor or using an ad hoc one
val extractors: Set[Item => Any] = Set(month, itemType, quantity, description)
}
Then you can pass those around like so:
val byMonth = makeMap[String](items)(Item.month)
The only real change semantically is that you explicitly avoid possible extra construction of lambdas at runtime, at the cost of having the lambdas stick around in memory the whole time. A fringe benefit is that you might be able to cache the maps by extractor if you're sure that the source Items never change: for lambdas, equality is reference equality. This might be particularly useful if you have some class representing the collection of Items as opposed to just using a standard collection, like so:
object Items {
def makeMap[T](items: Iterable[Item])(extractKey: Item => T): Map[T,
Iterable[Item]] =
items.groupBy(extractKey)
}
class Items(val underlying: immutable.Seq[Item]) {
def makeMap[T](extractKey: Item => T): Map[T, Iterable[Item]] =
if (Item.extractors.contains(extractKey)) {
if (extractKey == Item.month) groupedByMonth.asInstanceOf[Map[T, Iterable[Item]]]
else if (extractKey == Item.itemType) groupedByItemType.asInstanceOf[Map[T, Iterable[Item]]]
else if (extractKey == Item.quantity) groupedByQuantity.asInstanceOf[Map[T, Iterable[Item]]]
else if (extractKey == Item.description) groupedByDescription.asInstanceOf[Map[T, Iterable[Item]]]
else throw new AssertionError("Shouldn't happen!")
} else {
Items.makeMap(underlying)(extractKey)
}
lazy val groupedByMonth = Items.makeMap[String](underlying)(Item.month)
lazy val groupedByItemType = Items.makeMap[String](underlying)(Item.itemType)
lazy val groupedByQuantity = Items.makeMap[Int](underlying)(Item.quantity)
lazy val groupedByDescription = Items.makeMap[String](underlying)(Item.description)
}
(that is almost certainly a personal record for asInstanceOfs in a small block of code... I'm not sure if I should be proud or ashamed of this snippet)

Scala Lists and Option

I must be doing something wrong. I come form a Java background so this stuff should be easy.
I'm wanting to create a mapping between a key and multiple values held in a list:
var keys = Map[String, ListBuffer[String]]()
However, I can't seem to add a value to the list!!! What am I doing wrong??
def put(key: String, value: String) = {
var valueOption = keys.get(key)
var values = valueOption.getOrElse(ListBuffer)
values += value
// value not added
}
I do not want to use a MultiMap because I need to do some other operations which are not easy to do with a MultiMap.
Please help.
Thanks
The other answers are right about how you're not putting the new ListBuffer back in the Map, but their example code is verbose. A mutable Map has getOrElse and getOrElseUpdate methods for this. Also, use val not var for locals and the keys member, unless you have reason not to. I sometimes prefer append to +=.
def put(key: String, value: String) = {
keys.getOrElseUpdate(key, ListBuffer()) += value
}
The problem is here:
var valueOption = keys.get(key)
var values = valueOption.getOrElse(ListBuffer)
For any nonexistent key, keys.get will return a None Option. You then call getOrElse, and since the "else" part is used (because it's a None), a new ListBuffer is initialized. However, this is all that happens.
In particular, the new ListBuffer is NOT automatically put into the map. Such an operation wouldn't make sense - getOrElse is part of the Option API, it cannot "know" about any collection the Option is generated from.
To correct your problem, you have to put the new ListBuffer into the map yourself. An example if you're using a mutable Map:
def put(key: String, value: String) = {
var valueOption = keys.get(key)
var values = valueOption.getOrElse {val b = ListBuffer.empty[String]; keys.put(key,b); b;}
values += value
}
the problem is, that by calling getOrElse(ListBuffer) you do not insert the new ListBuffer into the Map. So you need to add an additional step:
def put(key: String, value: String) = {
var valueOption =
var values = keys.get(key) match {
case None => // key not yet defined
buffer = ListBuffer()
// insert into map!
keys += key -> buffer
buffer
case Some(buffer) => buffer // key is already defined just return it
}
values += value
}
Note that for keys += key -> buffer to work, i assume, that you use an mutable Map (import from scala.collection.mutable.Map) instad of the default immutable Map
getOrElse will return the default ListBuffer, which is an empty ListBuffer, if key doesn't exist. You will need to associate this with you key.

Case class equals with list of byte array

I am trying to use the should matchers on a case class
case class ListOfByteArrayCaseConfig(
#BeanProperty
permissions: java.util.List[Array[Byte]]
)
With the following test case
val orig = ListOfByteArrayCaseConfig(List(Array[Byte](10, 20, 30)))
val orig2 = ListOfByteArrayCaseConfig(List(Array[Byte](10, 20, 30)))
orig2 should be === orig
Obviously this would fail because the two byte arrays are not equal reference wise. What I want to do is somehow make this work without changing the test case code and still keeping the case class.
Is it even possible? (like adding a custom equals method to the case class?)
I found the solution. Apparently I can override the equals method in a case class
Scala: Ignore case class field for equals/hascode?
Though it gets rid of the reason for using case classes in the first place which is to simplify data objects.
case class ListOfByteArrayCaseConfig(
#BeanProperty
permissions: java.util.List[Array[Byte]]
) {
override def equals(arg: Any): Boolean = {
val obj = arg.asInstanceOf[ListOfByteArrayCaseConfig]
var i: Int = 0
for (i <- 0 until permissions.size()) {
if (!util.Arrays.equals(permissions.get(i), obj.permissions.get(i))) {
return false
}
}
return true
}
}

Filling a Scala immutable Map from a database table

I have a SQL database table with the following structure:
create table category_value (
category varchar(25),
property varchar(25)
);
I want to read this into a Scala Map[String, Set[String]] where each entry in the map is a set of all of the property values that are in the same category.
I would like to do it in a "functional" style with no mutable data (other than the database result set).
Following on the Clojure loop construct, here is what I have come up with:
def fillMap(statement: java.sql.Statement): Map[String, Set[String]] = {
val resultSet = statement.executeQuery("select category, property from category_value")
#tailrec
def loop(m: Map[String, Set[String]]): Map[String, Set[String]] = {
if (resultSet.next) {
val category = resultSet.getString("category")
val property = resultSet.getString("property")
loop(m + (category -> m.getOrElse(category, Set.empty)))
} else m
}
loop(Map.empty)
}
Is there a better way to do this, without using mutable data structures?
If you like, you could try something around
def fillMap(statement: java.sql.Statement): Map[String, Set[String]] = {
val resultSet = statement.executeQuery("select category, property from category_value")
Iterator.continually((resultSet, resultSet.next)).takeWhile(_._2).map(_._1).map{ res =>
val category = res.getString("category")
val property = res.getString("property")
(category, property)
}.toIterable.groupBy(_._1).mapValues(_.map(_._2).toSet)
}
Untested, because I don’t have a proper sql.Statement. And the groupBy part might need some more love to look nice.
Edit: Added the requested changes.
There are two parts to this problem.
Getting the data out of the database and into a list of rows.
I would use a Spring SimpleJdbcOperations for the database access, so that things at least appear functional, even though the ResultSet is being changed behind the scenes.
First, some a simple conversion to let us use a closure to map each row:
implicit def rowMapper[T<:AnyRef](func: (ResultSet)=>T) =
new ParameterizedRowMapper[T]{
override def mapRow(rs:ResultSet, row:Int):T = func(rs)
}
Then let's define a data structure to store the results. (You could use a tuple, but defining my own case class has advantage of being just a little bit clearer regarding the names of things.)
case class CategoryValue(category:String, property:String)
Now select from the database
val db:SimpleJdbcOperations = //get this somehow
val resultList:java.util.List[CategoryValue] =
db.query("select category, property from category_value",
{ rs:ResultSet => CategoryValue(rs.getString(1),rs.getString(2)) } )
Converting the data from a list of rows into the format that you actually want
import scala.collection.JavaConversions._
val result:Map[String,Set[String]] =
resultList.groupBy(_.category).mapValues(_.map(_.property).toSet)
(You can omit the type annotations. I've included them to make it clear what's going on.)
Builders are built for this purpose. Get one via the desired collection type companion, e.g. HashMap.newBuilder[String, Set[String]].
This solution is basically the same as my other solution, but it doesn't use Spring, and the logic for converting a ResultSet to some sort of list is simpler than Debilski's solution.
def streamFromResultSet[T](rs:ResultSet)(func: ResultSet => T):Stream[T] = {
if (rs.next())
func(rs) #:: streamFromResultSet(rs)(func)
else
rs.close()
Stream.empty
}
def fillMap(statement:java.sql.Statement):Map[String,Set[String]] = {
case class CategoryValue(category:String, property:String)
val resultSet = statement.executeQuery("""
select category, property from category_value
""")
val queryResult = streamFromResultSet(resultSet){rs =>
CategoryValue(rs.getString(1),rs.getString(2))
}
queryResult.groupBy(_.category).mapValues(_.map(_.property).toSet)
}
There is only one approach I can think of that does not include either mutable state or extensive copying*. It is actually a very basic technique I learnt in my first term studying CS. Here goes, abstracting from the database stuff:
def empty[K,V](k : K) : Option[V] = None
def add[K,V](m : K => Option[V])(k : K, v : V) : K => Option[V] = q => {
if ( k == q ) {
Some(v)
}
else {
m(q)
}
}
def build[K,V](input : TraversableOnce[(K,V)]) : K => Option[V] = {
input.foldLeft(empty[K,V]_)((m,i) => add(m)(i._1, i._2))
}
Usage example:
val map = build(List(("a",1),("b",2)))
println("a " + map("a"))
println("b " + map("b"))
println("c " + map("c"))
> a Some(1)
> b Some(2)
> c None
Of course, the resulting function does not have type Map (nor any of its benefits) and has linear lookup costs. I guess you could implement something in a similar way that mimicks simple search trees.
(*) I am talking concepts here. In reality, things like value sharing might enable e.g. mutable list constructions without memory overhead.

Scala Properties Question

I'm still learning Scala, but one thing I thought was interesting is that Scala blurs the line between methods and fields. For instance, I can build a class like this...
class MutableNumber(var value: Int)
The key here is that the var in the constructor-argument automatically allows me to use the 'value' field like a getter/setter in java.
// use number...
val num = new MutableNumber(5)
num.value = 6
println(num.value)
If I want to add constraints, I can do so by switching to using methods in place of the instance-fields:
// require all mutable numbers to be >= 0
class MutableNumber(private var _value: Int) {
require(_value >= 0)
def value: Int = _value
def value_=(other: Int) {
require(other >=0)
_value = other
}
}
The client side code doesn't break since the API doesn't change:
// use number...
val num = new MutableNumber(5)
num.value = 6
println(num.value)
My hang-up is with the named-parameter feature that was added to Scala-2.8. If I use named-parameters, my API does change and it does break the api.
val num = new MutableNumber(value=5) // old API
val num = new MutableNumber(_value=5) // new API
num.value = 6
println(num.value)
Is there any elegant solution to this? How should I design my MutableNumber class so that I can add constraints later on without breaking the API?
Thanks!
You can use the same trick that case classes do: use a companion object.
object Example {
class MutableNumber private (private var _value: Int) {
require (_value >= 0)
def value: Int = _value
def value_=(i: Int) { require (i>=0); _value = i }
override def toString = "mutable " + _value
}
object MutableNumber {
def apply(value: Int = 0) = new MutableNumber(value)
}
}
And here it is working (and demonstrating that, as constructed, you must use the object for creations, since the constructor is marked private):
scala> new Example.MutableNumber(5)
<console>:10: error: constructor MutableNumber cannot be accessed in object $iw
new Example.MutableNumber(5)
^
scala> Example.MutableNumber(value = 2)
res0: Example.MutableNumber = mutable 2
scala> Example.MutableNumber()
res1: Example.MutableNumber = mutable 0
Thanks for the answer! As an aside, I think the Scala-guys might be aware that there's an issue:
What's New in Scala 2.8: Named and Default Parameters
...
Until now, the names of arguments were a somewhat arbitrary choice for library developers, and weren't considered an important part of the API. This has suddenly changed, so that a method call to mkString(sep = " ") will fail to compile if the argument sep were renamed to separator in a later version.
Scala 2.9 implements a neat solution to this problem, but while we're waiting for that, be cautious about referring to arguments by name if their names may change in the future.
http://www.artima.com/scalazine/articles/named_and_default_parameters_in_scala.html
class MutableNumber {
private var _value = 0 //needs to be initialized
def value: Int = _value
def value_=(other: Int) {
require(other >=0) //this requirement was two times there
_value = other
}
}
you can modify all members of any class within curly braces
val n = new MutableNumber{value = 17}