An implementation of a string class that does case-sensitive comparisons in Scala

I have several classes with fields that should be case insensitive, and I would like to put instances of these classes in HashMaps and look at them using a string case insensitive.

Instead of using toLowerCase every time I want to index an instance by its row or search for an instance by its row, I instead tried to encapsulate this logic in the 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 came across is using junit assertEquals as follows:

 assertEquals("someString", instance.aCaseInsensitiveString) 

It fails, stating that it was expecting "someString", but received CaseInsensitiveString <"someString">.

If I reverse the order of the variables in assertEquals, then it works, perhaps because then it calls the equals function in the CaseInsensitiveString class. I'm currently working on this, keeping the order the same (so expected is actually expected), but call .toString on CaseInsensitiveString:

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

This also works:

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

Is it possible to add an implicit value equal to String to solve this problem?

+5
string scala
source share
5 answers

Here's a cleaner way to implement using proxies 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 for (string "== CaseInsensitive (" String ")).

You can implicitly convert like this:

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

Which should make it easy to compare:

  assertEquals("Hello"i, "heLLo"i) 
+7
source share

In Scala 2.8, you want to define Ordering[String] and override the compare method to compare case insensitive. You can then pass this (or define an implicit val) to any function that should perform the comparison - all standard collections accept Ordering[T] for their comparisons.

+3
source share

It seems to me that Java String.equalsIgnoreCase is what you need to use to solve the equality problem. Since JUnit is expecting a String, make sure you are a class derived from String so that it solves the problem. Also, remember the symmetric equality property, if a == b, then b == a, the implication it has for programming is that if you have two objects, obj1 and obj2, then obj1.equals (obj2 ) == obj2. equals (obj1)

Verify that the code meets these restrictions.

0
source share

Here is an example of using Ordering (since 2.8)

 val s = List( "a", "d", "F", "B", "e") 

res0: List [String] = List (B, F, a, d, e)

 object CaseInsensitiveOrdering extends scala.math.Ordering[String] { def compare(a:String, b:String) = a.toLowerCase compare b.toLowerCase } 

specific CaseInsensitiveOrdering object

 val orderField = CaseInsensitiveOrdering 

orderField: CaseInsensitiveOrdering.type = CaseInsensitiveOrdering $ @ 589643bb

 s.sorted(orderField) 

res1: List [String] = List (a, B, d, e, F)

0
source share

Today I ran into this problem. Here is how I decided to solve this problem:

First, I declared an object of type Ordering for sorting:

 import scala.math.Ordering.StringOrdering object CaseInsensitiveStringOrdering extends StringOrdering { override def compare(x: String, y: String) = { String.CASE_INSENSITIVE_ORDER.compare(x,y) } } 

Then, when I created my TreeMap, I used this object as follows:

 val newMap = new TreeMap[String,AnyRef]()(CaseInsensitiveStringOrdering) 

This was with Scala 2.11.8 BTW.

0
source share