Ra Ka Ra Ka - 3 days ago 5
Scala Question

Understanding Scala Ordered[ ] trait to compare reference

Currently, I am learning Scala and now, I am having some confusions understanding Ordered traits to compare object.

Consider following example, which are my current understanding of comparing,

Case I,
class Example(var n: Int) extends Ordered[Example] {
// ...
def compare(that: Example) =
(this.n) - (that.n)
}

var obj1 = new Example(12)
var obj2 = new Example(12)
obj1 compare obj2 //return 0 i.e. obj1 and obj2 are equal.

Case II,
class Example(var m: Int, var n: Int) extends Ordered[Example] {
// ...
def compare(that: Example) =
(this.m * this.n) - (that.m * that.n)
}

var obj1 = new Example(1, 2)
var obj2 = new Example(1, 2)
obj1 compare obj2 //return 0 i.e. obj1 and obj2 are equal.

Case III,
class Example(var name: String) extends Ordered[Example] {
// ...
def compare(that: Example) =
this.name compare that.name
}

var obj1 = new Example("abc")
var obj2 = new Example("abc)
obj1 compare obj2 //return 0 i.e. obj1 and obj2 are equal.


Case IV,
class Example(var name1: String, var name2: String) extends Ordered[Example] {
// ...
def compare(that: Example) =
(this.name1 + this.name2) compare (that.name1+that.name2)
}

var obj1 = new Example("abc","def")
var obj2 = new Example("abc","def")
obj1 compare obj2 //return 0 i.e. obj1 and obj2 are equal.


So, my questions is, what if there is non-constructor field in the class? for example,

class Example(var n: Int) extends Ordered[Example] {
var someVar: String = "default";
// ...
def compare(that: Example) =
(this.n) - (that.n)
//do we also need to compare someVar???? otherwise, object will have different state right??
}

var obj1 = new Example(12)
obj1.someVar = "value 1"
var obj2 = new Example(12)
obj2.someVar = "another value 2"
obj1 compare obj2 //Again, return 0 i.e. obj1 and obj2 are equal.


Please, correct me if above understanding are wrong.

Answer

In your compare method you are only comparing the n value of the instances. So, the instances with same n and different someVar will be considered similar. In order to make comparison based on both n and someVar take into account both n value and someVar in the compare method.

Here is the code how to do this

class Example(var n: Int) extends Ordered[Example] {
  var someVar: String = "default"
  def compare(that: Example) = {
    (this.n) - (that.n) match {
      case 0 => this.someVar.compare(that.someVar)
      case other => other
    }
  }
}

When there is a tie between the n's of different examples fallback to the string comparison. All that you have to do is just call compare on string instance.

Fallback to comparison of secondary fields if primary fields are equal and so on till you run out of fields to compare.

More idiomatic way to compare

 class Example(var n: Int) extends Ordered[Example] {
   var someVar: String = "default"

   import scala.math.Ordered.orderingToOrdered

   def compare(that: Example) = (this.n, this.someVar)  compare (that.n, that.someVar) 
 }

Create tuples and call compare on tuples, do not forget to import scala.math.Ordered.orderingToOrdered.

General information

compare returns zero or +ve or -ve integer. This way the algorithm can figure out the ordering of the collection of objects which implement ordering trait. When comparing two instances which extend Ordered trait and based on the integer returned by the compare method, the algorithm might know which instance of a particular type should be placed first while ordering or sorting.This is very common in Scala collections for sorting.

Scala REPL

notice that compare method

scala> case class A(a: Int) extends AnyVal  with Ordered[A] { override def compare(that: A): Int =  this.a - that.a }
defined class A

Notice the sorted order, its ascending order.

scala> List(A(1), A(2)).sorted
res4: List[A] = List(A(1), A(2))

Now, I return negation of the result after subtracting, notice order will be reversed now.

scala> case class A(a: Int) extends AnyVal  with Ordered[A] { override def compare(that: A): Int =  - (this.a - that.a) }
defined class A

Notice that reversed order

scala> List(A(1), A(2)).sorted
res5: List[A] = List(A(2), A(1))

Notice that if compare returns 0 that does not mean that objects are equal. You can return 0 inside compare method when objects are not equal to give equal priority while ordering or sorting.

Comments