I have created a class Path which should be uniquely identifiable by occupation and last.
case class Path(occupation: BitSet, last: Int) {
var cost = 0
def setCost(cost: Int) {
this.cost = cost
}
def getCost(): Int = {
return cost
}
}
}
Also, I would like it to be sortable by count, which I made a field.
implicit val pathOrd = Ordering.by((p: Path) => p.getCost)
The problem is that when I am sorting it(as you can see in the line above), I get java.lang.NullPointerException on that line.
Why is this happening?
Can I store my data better?
Your code gives no exceptions to me using this code:
# class Path(occupation: BitSet, last: Int) {
var cost = 0
def setCost(cost: Int) {
this.cost = cost
}
def getCost(): Int = {
return cost
}
}
defined class Path
# List(new Path(BitSet(), 3))
res6: List[Path] = List(cmd5$Path#3ef8de39)
# implicit val pathOrd = Ordering.by((p: Path) => p.getCost)
pathOrd: Ordering[Path] = scala.math.Ordering$$anon$9#5f478e42
# res6.sorted
res9: List[Path] = List(cmd5$Path#3ef8de39)
You are missing val for occupation and last
class Path(val occupation: BitSet, val last: Int)
I would suggest you to create a case class
case class Path(occupation: BitSet, last: Int)
It will have equals, and hashCode based on it's fields, toString, apply, unapply and copy methods.
I am not sure if you really need to modify the cost, if it is calculated based on other values you could make it a method
case class Path(occupation: BitSet, last: Int) {
def cost: Int = 42
}
if it's not then it should be a field. I want to encourage you to use immutable structures, which would mean doing:
case class Path(occupation: BitSet, last: Int, cost: Int)
Adding a field with setter and getter in scala is as simple as this:
class Path(val occupation: BitSet, val last: Int) {
var cost = 0
}
You can use it like this:
val path = new Path(BitSet(), 3)
path.cost = 12
println(path.cost)
Here is a simpler version of the code that uses Scala's conversions for using var to indicate mutable state in a class:
case class Path
(occupation: BitSet, // Immutable (will never change)
var cost: Int = 0, // Mutable
var last: Int = 0 // Mutable
)
// Implementing the Ordering TypeClass
implicit val pathOrd = Ordering.by((p: Path) => p.cost)
val t = new Path(BitSet(1,2), 0, 0)
t.cost = 2 // changing cost
val data = Seq(new Path(BitSet(1,2), 0, 0), new Path(BitSet(1), 3, 0), new Path(BitSet(1), 2, 0))
println(data.sorted)
Related
Is there a way to copy a case class in scala using a map of fields and their new values?
I tried a way that included reflection so I would like to avoid that
def copyWithMap(fieldNameValue: Map[String, Option[AnyREF]]): T
when applied to a case class this should make a copy and update the fields that need updating. (specified in the map)
If you want to do this for any generic object then you need some way of mapping the compile-time name of a field to the run-time location of that field in the object. The only built-in mechanism for this is reflection, so you can't do what you want without using reflection (or implementing your own generic reflection mechanism, which seems pointless).
If you know what you have to update before you start, you can do something like this:
scala> case class Person(name: String, age: Int, eyeColour: String)
defined class Person
scala> val p1 = Person("Bill", 24, "blue")
p1: Person = Person(Bill,24,blue)
scala> val p2 = p1.copy(name = "Ben", eyeColour = "brown")
p2: Person = Person(Ben,24,brown)
If you want to make it more generic, maybe something like this would work (setField taken from Duncan McGregor's answer in the linked post and put in implicit class):
implicit class Modify[T](i: T) {
def modify(m: Map[String, Any]): T = {
for ((name, value) <- m) setField(name, value)
i
}
def setField(fieldName: String, fieldValue: Any) = {
i.getClass.getDeclaredFields.find(_.getName == fieldName) match {
case Some(field) =>
field.setAccessible(true)
field.set(i, fieldValue)
case None =>
throw new IllegalArgumentException(s"No field named $fieldName")
}
}
}
case class Person(name: String, age: Int, eyeColour: String)
val p1 = Person("Bill", 24, "blue")
val p2 = p1.copy().modify(Map("name" -> "Ben", "eyeColour" -> "brown"))
// p2: Person = Person(Ben,24,brown)
p1
// res0: Person = Person(Bill,24,blue)
In Scala if I create a class like this:
class Time(var hour: Int) {
if (hour < 0) hour = 0
}
I can comfortably create a new object using
x = new Time(4)
If I need to get the time for the x object I can do
x.hour and I get back res5: Int = 4 which is cool. But considering I want to change the hour variable of x is doing x.hour = 5 enough? I think so. Is there another way to do this.
My main question is. How would I create another object, if i didn't want to use the new keyword?
As others mentioned, you can use case classes, but case class with var constructor parameter is usually a bad choice, as it meant to be immutable. To simulate changes you can use auto-generated copy method (it became more useful, if you have multiple parameters).
scala> case class Time(hour: Int) { require(hour > 0) }
defined class Time
scala> Time(-1)
java.lang.IllegalArgumentException: requirement failed
...
scala> val t1 = Time(1)
t1: Time = Time(1)
scala> val t2 = t1.copy(hour = 2)
t2: Time = Time(2)
For more complex sample, you can check this question.
You can use a case class if you want to omit the new keyword:
case class Time(var hour: Int) {
if (hour < 0) hour = 0
}
val today = Time(-1) //> today : Time = Time(0)
Case classes can also be used in pattern matching with case statements.
today match {
case Time(0) => "foo"
case Time(1) => "bar"
} //> res0: String = foo
Here is another SO post that talks about case classes: Link
You cannot create another object without directly or indirectly using the new keyword. Case classes, seems to allow that:
case class Person(name: String)
val p1 = Person("John")
However, that case class translates into something like this:
class Person(val name: String) {
override def equals(other: AnyRef): Boolean = ???
override def hashCode: Int = ???
override def toString: String = s"Person($name)"
}
object Person {
def apply(name: String) = new Person(name)
def unapply(person: Person): Option[String] = ???
}
val p1 = Person.apply("John")
So there's an implicit new there.
First question: Yes. Run scala in a terminal and you'll get the REPL, in which you can try out stuff like this yourself.
scala> class Time(var hour: Int) {
| if (hour < 0) hour = 0
| }
defined class Time
scala> val x = new Time(4)
x: Time = Time#bcc8d5
scala> x.hour = 5
x.hour: Int = 5
scala> x.hour
res0: Int = 5
Second question: It's a little unclear what you're asking for, but I think it's this:
scala> :paste
// Entering paste mode (ctrl-D to finish)
class Time(var hour: Int) {
if (hour < 0) hour = 0
}
object Time {
def apply(hour: Int): Time = new Time(hour)
}
// Exiting paste mode, now interpreting.
defined class Time
defined module Time
scala> val y = Time(7)
y: Time = Time#7359fe
scala> y.hour
res1: Int = 7
Is it possible to do that in Scala using only val:
class MyClass {
private val myVal1: MyClass2 //.....????? what should be here?
def myMethod1(param1: Int) = {
myVal1 = new MyClass2(param1)
//....
// some code....
}
}
The idea is that I can't initialize myVal1 immediately since the argument for its constructor is unknown yet and I have to do that in myMethod1. myVal1 should be visible withing a class and should be immutable.
No mutable state allowed.
So is it possible?
No, it isn't possible to do in the way you want. Consider, what would be the result of
val mc = new MyClass
mc.method1(0)
mc.method1(1)
? An exception thrown for setting myVal1 twice? Or should it keep the first value?
This is not possible, but there are some ways (in addition to using param1 as a constructor parameter)
Change the var into an Option; the setter myMethod1 returns a new instance of the same class with the Option set to the value.
Create a separate mutable Builder class with a var, and turn it into an immutable one later, when all data has been collected
If you are dealing with forward or cyclic references, consider using call-by-name and lazy vals (example 1, example 2)
Update: Example for 1:
class MyClass(val myVal1: Option[Int]) {
def myMethod1(param1: Int): MyClass = {
new MyClass(Some(param1))
}
}
object MyClass {
def apply() = new MyClass(None)
def apply(i: Int) = new MyClass(Some(i))
}
This pattern is used by immutable.Queue for example.
Update: Example for 3 (cyclic reference):
// ref ... call by name
class MyClass(val id: Int, ref: => MyClass) {
lazy val myVal1 = ref
override def toString: String = s"$id -> ${myVal1.id}"
}
to be used like this:
val a: MyClass = new MyClass(1, b)
val b: MyClass = new MyClass(2, a)
println(a)
println(b)
Update: Example for 3 (forward reference):
class MyClass2(val id: Int)
// ref ... call by name
class MyClass(val id: Int, ref: => MyClass2) {
lazy val myVal1 = ref
override def toString: String = s"$id -> ${myVal1.id}"
}
to be used with
val a = new MyClass(1, x)
println(a.id) // You can use a.id, but not yet the lazy val
val x = new MyClass2(10)
println(a)
To imitate a lazy "value" whose initial value might not be retrieved until after instance initialization completes (btw, there is nothing special about such objects, e.g. Swift have lazy properties that are even recommended to be declared as variables), you can introduce a wrapper to repeat the same logic that the Scala compiler generates internally for lazy values in Scala:
class LazyVar[T] {
private[this] var value$compute: () => T = () => null.asInstanceOf[T]
#volatile private[this] var value$: T = null.asInstanceOf[T]
#volatile private[this] var isInitialized$ = false
#volatile private[this] var isComputed$ = false
def value_=(value: T) = this.synchronized {
if(!isInitialized$) {
value$compute = () => value
isInitialized$ = true
}
else throw new IllegalStateException("Already initialized")
}
def value: T = this.synchronized {
if(!isInitialized$) throw new IllegalStateException("Not yet initialized")
else if(isComputed$) value$
else {
value$ = value$compute()
isComputed$ = true
value$
}
}
}
Now you just have to change MyClass2 to LazyVar[MyClass2] keeping tha val keyword as you wanted:
case class MyClass2(param: Int)
class MyClass {
private val myVal1: LazyVar[MyClass2] = new LazyVar[MyClass2]
def this(param: Int) {
this()
println("Storing the result of an expensive function...")
myVal1.value = new MyClass2(param)
}
def debug() = println(myVal1.value)
}
Now, if you write something like
val myClass = new MyClass(42)
myClass.debug
myClass.debug
you'll see the value is only computed once:
Storing the result of an expensive function...
MyClass2(42)
MyClass2(42)
I have big object:
case class BigObject(val str: String, val number: Int) {
val someVal = ...
val someVal2 = ...
}
I'd like to copy this object without re-valuation of values. Is it possible? Right now I'm using this approach:
val newBigObject = oldBigObject.copy(str = newStr)
As I see from the logs/debugger, "someVal" and "someVal2" are re-valuated. Is it possible to avoid it? As my BigObject is really big and value re-valuation takes some time, but performance is really important for me.
Thanks for your answers!
Here's a way:
Make the someVal and someVal2 fields which are also passed to the constructor and pull out the initialization logic for those fields in the companion object.
In your case:
class BigObject private(val str: String,
val number: Int,
val someVal: SomeType,
val someVal2: SomeType) {
def copy(newStr: String = str, newNumber: Int = number) = {
new BigObject(newStr, newNumber, someVal, someVal2)
}
}
object BigObject {
def apply(str: String, number: Int): BigObject = {
val someVal = initialize() //your initialization logic here
val someVal2 = initialize2()
new BigObject(str, number, someVal, someVal2)
}
}
Now, you can copy without re-evaling the inner fields:
val bigObj = BigObject("hello", 42)
val anotherBigObj = bigObj.copy(newStr = "anotherStr")
Alternatively, if you don't like companion objects, you can make two constructors. The primary one includes all the fields (also the non visible ones) and will be private. The public one will have only the two visible parameters:
class BigObject private(val str: String,
val number: Int,
val someVal: Any,
val someVal2: Any) {
def this(str: String, number: Int) = this(str, number, initializeVal, initializeVal2)
def copy(newStr: String = str, newNumber: Int = number) = {
new BigObject(newStr, newNumber, someVal, someVal2)
}
}
Usage:
val bigObj = new BigObject("hello", 42)
val anotherBigObj = bigObj.copy(newStr = "anotherStr")
If you have a case class like:
case class Foo(x: String, y: String, z: String)
And you have two instances like:
Foo("x1","y1","z1")
Foo("x2","y2","z2")
Is it possible to merge instance 1 in instance 2, except for field z, so that the result would be:
Foo("x1","y1","z2")
My usecase is just that I give JSON objects to a Backbone app through a Scala API, and the Backbone app gives me back a JSON of the same structure so that I can save/update it. These JSON objects are parsed as case class for easy Scala manipulation. But some fields should never be updated by the client side (like creationDate). For now I'm doing a manual merge but I'd like a more generic solution, a bit like an enhanced copy function.
What I'd like is something like this:
instanceFromDB.updateWith(instanceFromBackbone, excludeFields = "creationDate" )
But I'd like it to be typesafe :)
Edit:
My case class have a lot more fields and I'd like the default bevavior to merge fields unless I explicitly say to not merge them.
What you want is already there; you just need to approach the problem the other way.
case class Bar(x: String, y: String)
val b1 = Bar("old", "tired")
val b2 = Bar("new", "fresh")
If you want everything in b2 not specifically mentioned, you should copy from b2; anything from b1 you want to keep you can mention explicitly:
def keepY(b1: Bar, b2: Bar) = b2.copy(y = b1.y)
scala> keepY(b1, b2)
res1: Bar = Bar(new,tired)
As long as you are copying between two instances of the same case class, and the fields are immutable like they are by default, this will do what you want.
case class Foo(x: String, y: String, z: String)
Foo("old_x", "old_y", "old_z")
// res0: Foo = Foo(old_x,old_y,old_z)
Foo("new_x", "new_y", "new_z")
// res1: Foo = Foo(new_x,new_y,new_z)
// use copy() ...
res0.copy(res1.x, res1.y)
// res2: Foo = Foo(new_x,new_y,old_z)
// ... with by-name parameters
res0.copy(y = res1.y)
// res3: Foo = Foo(old_x,new_y,old_z)
You can exclude class params from automatic copying by the copy method by currying:
case class Person(name: String, age: Int)(val create: Long, val id: Int)
This makes it clear which are ordinary value fields which the client sets and which are special fields. You can't accidentally forget to supply a special field.
For the use case of taking the value fields from one instance and the special fields from another, by reflectively invoking copy with either default args or the special members of the original:
import scala.reflect._
import scala.reflect.runtime.{ currentMirror => cm }
import scala.reflect.runtime.universe._
import System.{ currentTimeMillis => now }
case class Person(name: String, age: Int = 18)(val create: Long = now, val id: Int = Person.nextId) {
require(name != null)
require(age >= 18)
}
object Person {
private val ns = new java.util.concurrent.atomic.AtomicInteger
def nextId = ns.getAndIncrement()
}
object Test extends App {
/** Copy of value with non-defaulting args from model. */
implicit class Copier[A: ClassTag : TypeTag](val value: A) {
def copyFrom(model: A): A = {
val valueMirror = cm reflect value
val modelMirror = cm reflect model
val name = "copy"
val copy = (typeOf[A] member TermName(name)).asMethod
// either defarg or default val for type of p
def valueFor(p: Symbol, i: Int): Any = {
val defarg = typeOf[A] member TermName(s"$name$$default$$${i+1}")
if (defarg != NoSymbol) {
println(s"default $defarg")
(valueMirror reflectMethod defarg.asMethod)()
} else {
println(s"def val for $p")
val pmethod = typeOf[A] member p.name
if (pmethod != NoSymbol) (modelMirror reflectMethod pmethod.asMethod)()
else throw new RuntimeException("No $p on model")
}
}
val args = (for (ps <- copy.paramss; p <- ps) yield p).zipWithIndex map (p => valueFor(p._1,p._2))
(valueMirror reflectMethod copy)(args: _*).asInstanceOf[A]
}
}
val customer = Person("Bob")()
val updated = Person("Bobby", 37)(id = -1)
val merged = updated.copyFrom(customer)
assert(merged.create == customer.create)
assert(merged.id == customer.id)
}
case class Foo(x: String, y: String, z: String)
val foo1 = Foo("x1", "y1", "z1")
val foo2 = Foo("x2", "y2", "z2")
val mergedFoo = foo1.copy(z = foo2.z) // Foo("x1", "y1", "z2")
If you change Foo later to:
case class Foo(w: String, x: String, y: String, z: String)
No modification will have to be done. Explicitly:
val foo1 = Foo("w1", "x1", "y1", "z1")
val foo2 = Foo("w2", "x2", "y2", "z2")
val mergedFoo = foo1.copy(z = foo2.z) // Foo("w1", "x1", "y1", "z2")