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?
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)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With