Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implementing a string class that does case insensitive comparisions in Scala

Tags:

string

scala

I've got a number of classes with fields that are meant to be case insensitive, and I'd like to put the instances of these classes into HashMaps and look them up by string case insensitive.

Instead of using toLowerCase every time I want to index an instance by its string, or look up an instance by its string, I've instead tried encapsulate this logic in a CaseInsensitiveString class:

/** Used to enable us to easily index objects by string, case insensitive
 * 
 * Note: this class preservse the case of your string!
 */
class CaseInsensitiveString ( val _value : String ) {
  override def hashCode = _value.toLowerCase.hashCode
  override def equals(that : Any) = that match {
    case other : CaseInsensitiveString => other._value.toLowerCase ==_value.toLowerCase
    case other : String => other.toLowerCase == _value.toLowerCase
    case _ => false
  }
  override def toString = _value
}

object CaseInsensitiveString {
  implicit def CaseInsensitiveString2String(l : CaseInsensitiveString) : String = if ( l ==null ) null else l._value
  implicit def StringToCaseInsensitiveString(s : String) : CaseInsensitiveString = new CaseInsensitiveString(s)

  def apply( value : String ) = new CaseInsensitiveString(value)
  def unapply( l : CaseInsensitiveString) = Some(l._value)
}

Can anyone suggest a cleaner or better approach?

One drawback I've come across is when using junit's assertEquals like this:

assertEquals("someString", instance.aCaseInsensitiveString)

It fails, saying it expected "someString" but got CaseInsensitiveString<"someString">.

If I reverse the order of the variables in the assertEquals, then it works, probably because its then calling the equals function on the class CaseInsensitiveString. I currently work around this by keeping the order the same (so the expected one is actually the expected one) but call .toString on the CaseInsensitiveString:

assertEquals("someString", instance.aCaseInsensitiveString.toString)

This works too:

assertEquals(CaseInsensitiveString("someString"), instance.aCaseInsensitiveString)

Is it possible for me to add an implicit equals to String to solve this?

like image 488
Alex Black Avatar asked Nov 17 '09 00:11

Alex Black


1 Answers

Here is a cleaner way of implementing using the "Proxy" and "Ordered" traits:

// http://www.scala-lang.org/docu/files/api/scala/Proxy.html
// http://www.scala-lang.org/docu/files/api/scala/Ordered.html


case class CaseInsensitive(s: String) extends Proxy with Ordered[CaseInsensitive] {
  val self: String = s.toLowerCase
  def compare(other: CaseInsensitive) = self compareTo other.self
  override def toString = s
  def i = this // convenience implicit conversion
}

No help on the ("string" == CaseInsensitive("String")) issue.

You can implicitly convert like so:

  implicit def sensitize(c: CaseInsensitive) = c.s
  implicit def desensitize(s: String) = CaseInsensitive(s)

Which should allow easy comparisons:

  assertEquals("Hello"i, "heLLo"i)
like image 118
Mitch Blevins Avatar answered Sep 18 '22 18:09

Mitch Blevins