Реализация строкового класса, который делает сравнения без учета регистра в Scala
У меня есть несколько классов с полями, которые должны быть нечувствительными к регистру, и я хотел бы поместить экземпляры этих классов в HashMaps и искать их по строке без учета регистра.
Вместо того чтобы использовать toLowerCase каждый раз, когда я хочу индексировать экземпляр по его строке или искать экземпляр по его строке, я вместо этого попытался инкапсулировать эту логику в классе CaseInsensitiveString:
/** 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)
}
Кто-нибудь может предложить более чистый или лучший подход?
Один недостаток, с которым я столкнулся, заключается в использовании assertEquals в junit, например:
assertEquals("someString", instance.aCaseInsensitiveString)
Это терпит неудачу, говоря, что ожидал "someString", но получил CaseInsensitiveString<"someString">.
Если я переверну порядок переменных в assertEquals, то это сработает, вероятно, потому, что тогда он вызывает функцию equals для класса CaseInsensitiveString. В настоящее время я работаю над этим, сохраняя порядок таким же (так что ожидаемый является фактически ожидаемым), но вызываю.toString для CaseInsensitiveString:
assertEquals("someString", instance.aCaseInsensitiveString.toString)
Это тоже работает:
assertEquals(CaseInsensitiveString("someString"), instance.aCaseInsensitiveString)
Могу ли я добавить неявное равенство в String, чтобы решить эту проблему?
5 ответов
Вот более чистый способ реализации с использованием черт "Proxy" и "Ordered":
// 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
}
Нет справки по ("string" == CaseInsensitive("String")) проблеме.
Вы можете неявно конвертировать так:
implicit def sensitize(c: CaseInsensitive) = c.s
implicit def desensitize(s: String) = CaseInsensitive(s)
Что должно позволить легко сравнивать:
assertEquals("Hello"i, "heLLo"i)
В Scala 2.8 вы хотите определить Ordering[String]
и переопределить compare
метод сравнения без учета регистра. Затем вы можете передать это (или определить неявное значение val) любой функции, которая нуждается в сравнении - все стандартные коллекции принимают Ordering[T]
для их сравнения.
Я столкнулся с этим вопросом сегодня. Вот как я решил это решить:
Сначала я объявил объект типа Ordering для сортировки:
import scala.math.Ordering.StringOrdering
object CaseInsensitiveStringOrdering extends StringOrdering {
override def compare(x: String, y: String) = {
String.CASE_INSENSITIVE_ORDER.compare(x,y)
}
}
Затем, когда я создал свой TreeMap, я использовал этот объект следующим образом:
val newMap = new TreeMap[String,AnyRef]()(CaseInsensitiveStringOrdering)
Это было с Scala 2.11.8 Кстати.
Вот пример использования Ordering (начиная с версии 2.8)
val s = List( "a", "d", "F", "B", "e")
res0: List [String] = Список (B, F, a, d, e)
object CaseInsensitiveOrdering extends scala.math.Ordering[String] {
def compare(a:String, b:String) = a.toLowerCase compare b.toLowerCase
}
определенный объект CaseInsensitiveOrdering
val orderField = CaseInsensitiveOrdering
orderField: CaseInsensitiveOrdering.type = CaseInsensitiveOrdering $ @ 589643bb
s.sorted(orderField)
res1: List [String] = Список (a, B, d, e, F)
Мне кажется, что Java String.equalsIgnoreCase - это то, что вам нужно использовать для решения проблемы равенства. Поскольку JUnit ожидает String, убедитесь, что ваш класс является производным от String, и это решит проблему. Кроме того, помните симметричное свойство равенства, если a == b, то b == a, это означает, что если вы имеете два объекта, obj1 и obj2, то obj1.equals(obj2) == obj2. равна (obj1)
Убедитесь, что ваш код соответствует этим ограничениям.