对Scala案例类执行不区分大小写的比较的最佳方法

时间:2015-09-25 00:34:50

标签: scala

我有一个代表Person的案例类。

case class Person(firstName: String, lastName: String)

我需要以不区分大小写的方式根据名字和姓氏进行人物比较,例如:

Person("John", "Doe") == Person("john", "Doe") // should return true

或在Seq中

Seq(Person("John", "Doe")).contains(Person("john", "Doe")

最简单的方法是在Person case类中覆盖equals和hashCode方法,但是如果在类的情况下不能覆盖equals和hashCode,那么以干净的方式执行此操作的最佳方法是什么。

有人可以推荐一种解决这种区分大小写问题的惯用方法吗?

谢谢, Suriyanto

7 个答案:

答案 0 :(得分:2)

对于案例类,我不会 $resultsall = array_map(function ($json) { return json_decode($json, true); }, $res); equals的原始含义,因此,hashCode==从功能编程的角度来看,恕我直言这里最惯用的解决方案是使用类型类:

case class Person(firstName: String, lastName: String)

trait Equal[A] {
  def eq(a1: A, a2: A): Boolean
}

object Equal {
  def areEqual[A : Equal](a1: A, a2: A): Boolean = implicitly[Equal[A]].eq(a1, a2)

  implicit object PersonEqual extends Equal[Person] {
    override def eq(a1: Person, a2: Person): Boolean = a1.firstName.equalsIgnoreCase(a2.firstName) &&
      a1.lastName.equalsIgnoreCase(a2.lastName)
  }
}

在REPL会话中:

scala> import Equal.areEqual
import Equal.areEqual

scala> val p1 = Person("John", "Doe")
p1: Person = Person(John,Doe)

scala> val p2 = p1.copy(firstName = "john")
p2: Person = Person(john,Doe)

scala> areEqual(p1, p2)
res0: Boolean = true

scala> val p3 = p1.copy(lastName = "Brown")
p3: Person = Person(John,Brown)

scala> areEqual(p1, p3)
res1: Boolean = false

这样,如果您需要在给定的上下文中为Person提供不同的相等含义,则可以实现Equal[Person]的版本而不触及任何其他内容。例如:在代码的给定点,如果它们具有相同的姓氏,则两个Person个实例是相等的:

implicit object PersonLastnameEqual extends Equal[Person] {
  override def eq(a1: Person, a2: Person): Boolean = a1.lastName.equalsIgnoreCase(a2.lastName)
}

REPL会议:

scala> val p1 = Person("John", "Doe")
p1: Person = Person(John,Doe)

scala> val p2 = p1.copy(firstName = "Mary")
p2: Person = Person(Mary,Doe)

scala> areEqual(p1, p2)
res0: Boolean = true

答案 1 :(得分:1)

你可以执行一些"规范化"在人员建设:

sealed trait Person {
  def firstName:String
  def lastName:String
}

object Person {
  def apply(firstName:String, lastName:String):Person = PersonNormalized(firstName.toLowerCase, lastName.toLowerCase)

  private case class PersonNormalized(firstName:String, lastName:String) extends Person
}

由您决定是否优于覆盖equals和hashCode

答案 2 :(得分:1)

  

最简单的方法是覆盖Person case类中的equals和hashCode方法,但是如果类不满意则重写equals和hashCode

如果您的实施符合the contract,则覆盖它们是完全可以接受的。唯一的问题是,如果Person发生变化,则需要更新,但其他解决方案也是如此。

答案 3 :(得分:0)

创建普通类(不是case)并为其编写equals / hashcode。 IDE-s通常会得到一些片段(因为它们在Java中是必需的)。

class Person(val firstName: String, val lastName: String) {
    override def hashCode = ???
    override def equals = ???
}

或者,如果我们要解决“XY问题”,您应该写出更广泛的关注/目标。也许,实际上,您应该只有Mapcase class - 仅使用较低的名称,或者在DB中使用Long令牌。猜猜......

答案 4 :(得分:0)

对于.nojekyll的所有成员的不区分大小写的比较,考虑其值的(较低的)字符串,如下所示,

Person

因此

case class Person(firstName: String, lastName: String) {
  def ==(that: Person) = {
    val thisStr = this.productIterator.mkString.toLowerCase
    val thatStr = that.productIterator.mkString.toLowerCase
    thisStr == thatStr
  }
}

答案 5 :(得分:0)

我不建议为案例类更改==的功能,但您可以创建一个具有不区分大小写比较含义的新运算符:

case class Person(firstName: String, lastName: String) {

  def toUpper = 
    this.copy(firstName = firstName.toUpperCase, lastName = lastName.toUpperCase)

  def =~=(that: Person) = this.toUpper == that.toUpper
}

Person("john", "smith") =~= Person("JoHn", "SmiTH") //true

Seq(
  Person("JoHn", "SmiTH"),
  Person("Jack", "Jones")
).exists(_ =~= Person("john", "smith")) //true

如果您想要比.exists更具体的内容:

implicit class PersonSeq(s: Seq[Person]) {

  def containsInsensitive(p: Person) = s.exists(_ =~= p)
}

Seq(
  Person("JoHn", "SmiTH"),
  Person("Jack", "Jones")
).containsInsensitive(Person("john", "smith"))

答案 6 :(得分:0)

另一种使用字符串插值的方法(参见https://stackoverflow.com/a/28445089/471136):

implicit class StringInterpolations(sc: StringContext) {
  def ci = new {
    def unapply(other: String) = sc.parts.mkString.equalsIgnoreCase(other)
  }
}

"Hello" match {
  case ci"Bye" => println("bad!")
  case ci"HELLO" => println("sweet!")
  case _ => println("fail!")
}

Person("John", "Doe") match {
  case Person(ci"john", ci"Doe") => 
  case _ => assert(false)
}