Yarh
Yarh

Reputation: 4607

Is there a way to efficiently compare fields of arbitary objects

Currently, if I need to compare objects by the specific fields I Implement DiffEqual interface

interface DiffEquals<T> {
  fun isItemSame(other: Any?): Boolean
  fun isContentSame(other: Any?): Boolean
}

some object

data class Book(
  val title: String, val amount: String, var isSelected: Boolean = false
) : DiffEquals {

  override fun isItemSame(other: Any?): Boolean {
    if (other != null && other is Book) {
      if (title != other.title) {
        return false
      }
      return true
    } else {
      return false
    }
  }

  override fun isContentSame(other: Any?): Boolean {
    other as Book
    if (amount != other.amount) return false
    if (isSelected != other.isSelected) return false

    return true
  }
}

I don't like that approach as it requires a lot of dummy code. I tried to make annotations like Id1, Id2, Id3, Mutable1, Mutable2, Mutable3 and so on, but it was resource costly, thus I had to rollback to interface above. How can one implement a generic comparison mechanism which compares specified fields? (not standard equals does not fit as more complex objects have fields which constantly changes, but are not important for equality, overriding equals creates same problems with dummy code and in most cases, I need full equals to)

Upvotes: 1

Views: 94

Answers (2)

Yarh
Yarh

Reputation: 4607

slight wariation of @Bananon answer using varargs

inline fun <reified T> T.equalTo(other: Any?, vararg properties: T.() -> Any?): Boolean {
  if (other !is T) return false
  properties.forEach {
    if (it.invoke(this) != it.invoke(other)) {
      return false
    }
  }
  return true
}

Upvotes: 0

IlyaMuravjov
IlyaMuravjov

Reputation: 2492

You can create inline equalsBy functions:

@PublishedApi
@Suppress("UNCHECKED_CAST")
internal inline fun <T> T.eqlBy(other: Any?, prop1: T.() -> Any?): Boolean = prop1() == (other as T).prop1()

inline fun <reified T> T.equalsBy(other: Any?, prop1: T.() -> Any?): Boolean =
    other is T && eqlBy(other, prop1)

inline fun <reified T> T.equalsBy(other: Any?, prop1: T.() -> Any?, prop2: T.() -> Any?): Boolean =
    equalsBy(other, prop1) && eqlBy(other, prop2)

inline fun <reified T> T.equalsBy(other: Any?, prop1: T.() -> Any?, prop2: T.() -> Any?, prop3: T.() -> Any?): Boolean =
    equalsBy(other, prop1, prop2) && eqlBy(other, prop3)

And use them like this:

data class Book(
    val title: String, val amount: String, var isSelected: Boolean = false
) : DiffEquals<Book> {
    override fun isItemSame(other: Book) = equalsBy(other) { title }
    override fun isContentSame(other: Book) = equalsBy(other, { amount }, { isSelected })
}

Upvotes: 1

Related Questions