I am trying to compare two Lists containing case classes. Can't figure out which method to override to get this working. Please refer to the code below.
trait Filter {
def toQueryString: String
}
trait SimpleFilter extends Filter {
val key: String
val value: Any
override def toQueryString = value match {
case v: String => s"$key='$v'"
case _ => s"$key=$value"
}
override def toString = "$key:$value"
override def equals(that: Any) = {
that match {
case s: SimpleFilter => {
key == key && value == value
}
case _ => false
}
}
override def hashCode() = key.hashCode + value.hashCode
}
class DestinationFilter(override val value: String) extends SimpleFilter {
override val key = "destination"
}
object DestinationFilter {
def apply(value: String) = new DestinationFilter(value)
}
object Tester {
def main(args: Array[String]): Unit = {
val d1 = DestinationFilter("Google")
val d2 = DestinationFilter("Google")
val l1 = List(d1)
val l2 = List(d2)
println(11 == 12)
println(d1 == d2)
}
}
which returns
false
true
Can't understand why the list comparison returns false. Ultimately what I want is this to be equal
List(DestinationFilter("Amazon"), LocationFilter("Yahoo"), DestinationFilter("Google") == List(DestinationFilter("Yahoo"), LocationFilter("Amazon"), DestinationFilter("Google")
*they should be equal irrespective of order.
You have a typo in your comparison, 11 and 12 should be l1 and l2:
println(l1 == l2) // true
So 11 == 12 is always false, you're just comparing Ints.
You also need to change your equals override, your just comparing key and value to themselves, which is always true:
override def equals(that: Any) = {
that match {
case s: SimpleFilter =>
key == s.key && value == s.value //edited
case _ => false
}
}
Related
I have the following class diagram and would like some help with refactoring.
The code:
trait id {
val id: String
def uuid = java.util.UUID.randomUUID.toString
}
abstract class node extends id {
def deepCopy: node
def hasSameId(n: node): Boolean = n.id == id
}
trait manifest {
val properties = new mutable.HashMap[String, Any]
val manifest: immutable.HashMap[String, (String, String)]
def isComplete: Boolean
def setProperty(k: String, v: Any): this.type
def getProperty(k: String) = properties(k)
def getAllProperties = properties
def hasEqualProperties(p: mutable.HashMap[String, Any]): Boolean = {
if (properties.size != p.size) false
else {
properties.map(prop => p(prop._1) == prop._2).foldLeft(true)((r, c) => r && c) // this will give trouble if off manifest property table have the same size but different optionals from an open manifest
}
}
}
trait sealedManifest extends manifest {
def setProperty(k: String, v: Any) = {
if (manifest.contains(k)) {
val keyType = manifest(k)._1
val valueType = v.getClass.getCanonicalName
if (keyType == valueType) properties(k) = v
else throw new IllegalArgumentException(""" Expecting type "%s" received type "%s" for "%s" """.format(keyType,valueType,k))
} // test for type as well
else throw new IllegalArgumentException(""" "%s" is not on manifest""".format(k))
this
}
}
trait openManifest extends manifest {
def setProperty(k: String, v: Any) = {
if (manifest.contains(k)) {
val keyType = manifest(k)._1
val valueType = v.getClass.getCanonicalName
if (keyType == valueType) properties(k) = v
else throw new IllegalArgumentException(""" Expecting type "%s" received type "%s" for "%s" """.format(keyType,valueType,k))
} // test for type as well
else properties(k) = v
this
}
}
case class system(val uid: String = null) extends node with openManifest {
val id = if (uid != null) uid else uuid
override val manifest = immutable.HashMap(
"name" -> ("java.lang.String","req"),
"description" -> ("java.lang.String","opt")
)
override def isComplete: Boolean = true
def CONNECTS(s: system, id: String = null) = {
new systemCONNECTSsystem(this, s, id)
}
def PRODUCES(d: dataset, id: String = null) = {
new systemPRODUCESdataset(this, d, id)
}
def <= (kv: (String, Any)): system = {
setProperty(kv._1,kv._2)
this
}
def deepCopy = {
val s = new system(id)
getAllProperties.foreach(p => s.setProperty(p._1,p._2))
s
}
def isIdenticalTo(s: system) = {
hasSameId(s) && hasEqualProperties(s.getAllProperties)
}
}
case class dataset(val uid: String = null) extends node with sealedManifest{
val id = if (uid != null) uid else uuid
override val manifest = immutable.HashMap(
"name" -> ("java.lang.String","req"),
"description" -> ("java.lang.String","opt")
)
override def isComplete = true
def <= (kv: (String, Any)): dataset = {
setProperty(kv._1,kv._2)
this
}
def deepCopy = {
val d = new dataset(id)
getAllProperties.foreach(p => d.setProperty(p._1,p._2))
d
}
def isIdenticalTo(d: dataset) = {
hasSameId(d) && hasEqualProperties(d.getAllProperties)
}
}
The use case is ultimately that many additional classes like 'system' and 'dataset' will be generated. I am seeing a lot of build up of boiler plate at this level notably in 'deepCopy' and 'isIdenticalTo'. That smells wrong and I think that these blocks of code can be moved to higher levels in the class diagram, but I am struggling to achieve this.
When I try to move the code to higher levels I run into problems because these methods are identical in everything except type. So I was hoping the type classes might help, or simply (multiple-)inheritance.
Can anyone help?
Here is an example from the stairway book:
object Example1 {
import collection._
class PrefixMap[T]
extends mutable.Map[String, T]
with mutable.MapLike[String, T, PrefixMap[T]] {
var suffixes: immutable.Map[Char, PrefixMap[T]] = Map.empty
var value: Option[T] = None
def get(s: String): Option[T] = {
// base case, you are at the root
if (s.isEmpty) value
// recursive
else suffixes get (s(0)) flatMap (_.get(s substring 1))
}
def iterator: Iterator[(String, T)] = {
(for (v <- value.iterator) yield ("", v)) ++
(for ((chr, m) <- suffixes.iterator; (s, v) <- m.iterator) yield (chr +: s, v))
}
def +=(kv: (String, T)): this.type = {
update(kv._1, kv._2)
this
}
def -=(key: String): this.type = {
remove(key)
this
}
def withPrefix(s: String): PrefixMap[T] = {
if (s.isEmpty) this
else {
val leading = s(0)
suffixes get leading match {
case None => {
// key does not exist, create it
suffixes = suffixes + (leading -> empty)
}
case _ =>
}
// recursion
suffixes(leading) withPrefix (s substring 1)
}
}
override def update(s: String, elem: T) = {
withPrefix(s).value = Some(elem)
}
override def remove(key: String): Option[T] = {
if (key.isEmpty) {
// base case. you are at the root
val prev = value
value = None
prev
} else {
// recursive
suffixes get key(0) flatMap (_.remove(key substring 1))
}
}
override def empty = PrefixMap.empty
}
import collection.mutable.{Builder, MapBuilder}
import collection.generic.CanBuildFrom
object PrefixMap {
def empty[T] = new PrefixMap[T]
def apply[T](kvs: (String, T)*): PrefixMap[T] = {
val m: PrefixMap[T] = empty
for(kv <- kvs) m += kv
m
}
def newBuilder[T]: Builder[(String, T), PrefixMap[T]] = {
new mutable.MapBuilder[String, T, PrefixMap[T]](empty)
}
implicit def canBuildFrom[T]: CanBuildFrom[PrefixMap[_], (String, T), PrefixMap[T]] = {
new CanBuildFrom[PrefixMap[_], (String, T), PrefixMap[T]] {
def apply(from: PrefixMap[_]) = newBuilder[T]
def apply() = newBuilder[T]
}
}
}
}
I don't understand this line:
def apply(from: PrefixMap[_]) = newBuilder[T]
What's point in receiving a PrefixMap and returning a empty PrefixMap?
Read little bit more official docs
If in short: CBF can return builder with knowledge of properties of whole collection.
For example it could preinitialize some buffer of needed size to collect entries.
Or even reuse some parts of collection of known type and structure.
But by default in many case it would just collect element by element to empty collection. That's happening in your case.
I'd like to put some data into a HashMap and retrieve these as typed values using a function. The function takes the expected type and also a default value in case the value is not stored in the HashMap. Type erasure of the JVM makes this a tricky thing.
Q: How can I retrieve a typed value?
Code and results below.
abstract class Parameters(val name: String) {
val parameters = new HashMap[String, Any]()
def put(key: String, value: Any) = parameters(key) = value
def get(key: String) = parameters.getOrElse(key, None)
def remove(key: String) = parameters.remove(key)
def g0[T: TypeTag](key: String, defaultValue: T) = {
get(key) match {
case x: T => x
case None => defaultValue
case _ => defaultValue
}
}
def g1[T: ClassTag](key: String, defaultValue: T) = {
val compareClass = implicitly[ClassTag[T]].runtimeClass
get(key) match {
case None => defaultValue
case x if compareClass.isInstance(x) => x.asInstanceOf[T]
}
}
}
class P extends Parameters("AParmList") {
put("1", 1)
put("3", "three")
put("4", 4.0)
put("width", 600)
println(g0[Int]("width", -1))
println(g0[Int]("fail", -2))
println(g1[Int]("width", -3))
println(g1[Int]("fail", -4))
}
object TypeMatching {
def main(args: Array[String]) {
new P
}
}
The output is (comments in parenthesis):
600 (as expected)
None (expected -2)
and a match error (java.lang.Integer stored, Int required)
I have the following class:
abstract class IRDMessage extends Ordered[IRDMessage] {
val messageType: MessageType.Type
val timestamp: DateTime
val content: AnyRef
def compare(that: IRDMessage): Int = {
val res = timestamp compareTo that.timestamp
res
}
override def equals(obj: Any): Boolean = obj match{
case message: IRDMessage => compareTo(message) == 0
case _ => false
}
}
I have a couple of concrete implementations as well. However, when I try to say a == b of any subtype of IRDMessage the equals method does not get called and it simply compares references (default equals implementation). Any ideas what might be causing this?
The subclasses are simple case classes btw.
This does actually work given the following simplified example:
object MessageTest {
def main(args: Array[String]) {
val m1 = MessageImpl("foo")
val m2 = MessageImpl("bar")
m1 == m2
}
}
abstract class IRDMessage extends Ordered[IRDMessage] {
val content: AnyRef
override def equals(obj: Any): Boolean = {
println("in here")
obj match{
case message: IRDMessage => compareTo(message) == 0
case _ => false
}
}
}
case class MessageImpl(content:String) extends IRDMessage{
override def compare(that:IRDMessage) = 0
}
Can you post a little more code, specifically one of your sample case classes? I noticed if I defined the case class like so:
case class MessageImpl extends IRDMessage{
var content = "foo"
override def compare(that:IRDMessage) = 0
}
It does not work as expected.
I have this setup where I want to populate an object fields from an XML NodeSeq. A cutdown version of my setup is shown. It works well, but when I put None in testObj, I get a java.util.NoSuchElementException: None.get.
The problem I believe is that I cannot find a way to get the class of the Option[_] when it is None. I've been stuck on this for ages and I cannot see a way out. I feel there must be a way to branch and populate the object field when the original value is None, but how?
import xml.NodeSeq
object test {
case class TestObject(name: Option[String] = None, value: Option[Double] = None)
def main(args: Array[String]): Unit = {
val testObj = TestObject(Some("xxx"), Some(12.0))
// val testObj = TestObject(None, Some(12.0))
val nodeSeq = <test> <name> yyy </name> <value> 34.0 </value> </test>
val result = getObject[TestObject](nodeSeq, Option(testObj))
println("result = " + result) // result = Some(TestObject(Some(yyy),Some(34.0)))
}
def getObject[A](nodeSeq: NodeSeq, obj: Option[A]): Option[A] = {
if ((nodeSeq.isEmpty) || (!obj.isDefined)) None else {
for (field <- obj.get.getClass.getDeclaredFields) {
field.setAccessible(true)
val fieldValue = field.get(obj.get)
if (fieldValue == null || !fieldValue.isInstanceOf[Option[_]]) None
var newValue: Option[_] = None
fieldValue.asInstanceOf[Option[_]].get match { // <---- None.get is the error here
case x: Double => newValue = Some((nodeSeq \ field.getName).text.trim.toDouble)
case x: String => newValue = Some((nodeSeq \ field.getName).text.trim)
}
val decField = obj.get.getClass.getDeclaredField(field.getName)
decField.setAccessible(true)
decField.set(obj.get, newValue)
}
obj
}
}
}
I'm ignoring some other parts of this code that I don't like, however, you can't call .get on a None. You can call map and it will return what I assume you want. Here's the modified code:
def getObject[A](nodeSeq: NodeSeq, obj: Option[A]): Option[A] = {
if (nodeSeq.isEmpty || obj.isEmpty) {
None
}
else {
for (field <- obj.get.getClass.getDeclaredFields) {
field.setAccessible(true)
val fieldValue = field.get(obj.get)
if (fieldValue == null || !fieldValue.isInstanceOf[Option[_]]) None
val newValue = fieldValue.asInstanceOf[Option[_]].map(a => {
a match {
case x: Double => Some((nodeSeq \ field.getName).text.trim.toDouble)
case x: String => Some((nodeSeq \ field.getName).text.trim)
}
})
val decField = obj.get.getClass.getDeclaredField(field.getName)
decField.setAccessible(true)
decField.set(obj.get, newValue)
}
obj
}
}
In practice, I usually avoid calling .get on options as it results in bugs quite often.
UPDATE:
This version is a little more functional, though it still alters the obj that you send in:
def getObject[A](nodeSeq: NodeSeq, obj: Option[A]): Option[A] = {
obj.map(o => {
for {
field <- o.getClass.getDeclaredFields
_ = field.setAccessible(true)
fieldValue <- Option(field.get(o))
} yield {
val newValue = fieldValue match {
case Some(a) => {
a match {
case x: Double => Some((nodeSeq \ field.getName).text.trim.toDouble)
case x: String => Some((nodeSeq \ field.getName).text.trim)
}
}
case _ => None
}
val decField = o.getClass.getDeclaredField(field.getName)
decField.setAccessible(true)
decField.set(o, newValue)
}
o
})
}
Actually, this might be closer to what you want: check the type of the field instead.
import xml.NodeSeq
object test {
case class TestObject(name: Option[String] = None, value: Option[Double] = None)
def main(args: Array[String]): Unit = {
val testObj = TestObject(Some("xxx"), Some(12.0))
// val testObj = TestObject(None, Some(12.0))
val nodeSeq = <test> <name> yyy </name> <value> 34.0 </value> </test>
val result = getObject[TestObject](nodeSeq, Option(testObj))
println("result = " + result) // result = Some(TestObject(Some(yyy),Some(34.0)))
}
def getObject[A](nodeSeq: NodeSeq, obj: Option[A]): Option[A] = {
if ((nodeSeq.isEmpty) || (!obj.isDefined)) None else {
for (field <- obj.get.getClass.getDeclaredFields) {
field.setAccessible(true)
val newValue = field.getGenericType match {
case t: ParametrizedType if (t.getActualType == classOf[Option[_]]) =>
val paramType = t.getActualTypeArguments()(0)
if (paramType == classOf[java.lang.Double]) // since Double can't be a generic type parameter on JVM
// from your original code, but it probably needs to be modified
// to handle cases where field isn't in nodeSeq or not a number
Some((nodeSeq \ field.getName).text.trim.toDouble)
else if (paramType == classOf[String])
Some((nodeSeq \ field.getName).text.trim)
else None
case _ => None
}
if (newValue.isDefined)
field.set(obj.get, newValue)
}
obj
}
}
}
Version creating a new object instead (might need modification for your requirements):
def getObject[A](nodeSeq: NodeSeq, objClass: Class[A]): Option[A] = {
if (nodeSeq.isEmpty) None else {
try {
val fieldValues = objClass.getDeclaredFields.flatMap { field =>
field.setAccessible(true)
field.getGenericType match {
case t: ParametrizedType if (t.getActualType == classOf[Option[_]]) =>
val paramType = t.getActualTypeArguments()(0)
if (paramType == classOf[java.lang.Double])
Some(Some((nodeSeq \ field.getName).text.trim.toDouble))
else if (paramType == classOf[String])
Some(Some((nodeSeq \ field.getName).text.trim))
else None
case _ => None
}
}
// assumes only one constructor, otherwise get the desired one
val ctor = objClass.getConstructors()(0)
Some(ctor.newInstance(fieldValues))
} catch {
case _ => None
}
}
}