equatable icon indicating copy to clipboard operation
equatable copied to clipboard

Add option to exclude properties for equal check

Open JulianBissekkou opened this issue 2 years ago • 3 comments

Is your feature request related to a problem? Please describe. I am doing an equal check on two instances and I want to have a nice syntax for excluding properties.

Describe the solution you'd like Since this might be a niche problem my suggestion would be to provide a static helper function that does this. I would not add this to the EqualtableMixin.

Here is a small example:

  static bool isEqualExcluding<T extends EquatableMixin>(
    T lhs,
    T rhs,
    Object Function(T)? excludeProperty,
  ) {
    if (excludeProperty == null) return lhs == rhs;

    final lhsProperties = lhs.props
        .where((property) => property == excludeProperty(lhs))
        .toList();
    final rhsProperties = rhs.props
        .where((property) => property == excludeProperty(rhs))
        .toList();

    return equatable_utils.equals(lhsProperties, rhsProperties);
  }

(The current implementation will not work if a class has two properties with the same value)

JulianBissekkou avatar Jul 07 '21 13:07 JulianBissekkou

just don't include that excluded properties on props list, i don't know what's the use case for this tbh

alvinvin00 avatar Sep 30 '21 05:09 alvinvin00

Excluding a property just for a specific operation. Excluding the property in the props list will affect all equal checks.

JulianBissekkou avatar Sep 30 '21 06:09 JulianBissekkou

I don't think that's possible. In your app you cant compare two objects of the same class using different combination of fields (directly), it would be hard to implement something so generic without some magic (like code generation, which some don't like).

However, you can always create helper methods, like this:

import 'package:equatable/equatable.dart';

class Person extends Equatable {
  final int age;
  final String name;

  Person(this.name, this.age);

  bool equalsByAge(Person other) {
    return age == other.age;
  }

  bool equalsByName(Person other) {
    return name == other.name;
  }

  @override
  List<Object> get props => [age, name];
}

void main() {
  final someBob = Person('Bob', 42);
  final anotherBob = Person('Bob', 42);
  final alice = Person('Alice', 69);

  // By all fields
  print(someBob == anotherBob); // true

  // By specific fields
  print(someBob.equalsByAge(anotherBob)); // true
  print(someBob.equalsByName(anotherBob)); // true
  print(someBob.equalsByName(alice)); // false
  print(someBob.equalsByAge(alice)); // false
}

But of course you can only use that for client equality tests, for collections you have to use something else. I've come up with this:

abstract class PersonEquality extends Person {
  PersonEquality(String name, int age) : super(name, age);
}

class PersonEqualityByAge extends PersonEquality {
  PersonEqualityByAge(String name, int age) : super(name, age);

  @override
  bool operator ==(Object other) {
    if (identical(this, other)) return true;
    if (other is! Person) return false;
    return age == other.age;
  }

  @override
  int get hashCode => age.hashCode;
}

class PersonEqualityByName extends PersonEquality {
  PersonEqualityByName(String name, int age) : super(name, age);

  @override
  bool operator ==(Object other) {
    if (identical(this, other)) return true;
    if (other is! Person) return false;
    return name == other.name;
  }

  @override
  int get hashCode => age.hashCode;
}

void main() {
  final people = <PersonEquality>{
    PersonEqualityByAge('John Doe', 69),
  };
  print(people.contains(PersonEqualityByAge('John', 69))); // true
  print(people.contains(PersonEqualityByName('John Doe', 69))); // true
  print(people.contains(PersonEqualityByAge('Johny', 18))); // false
}

As you can see it's not the most concise way of doing things, but I think, considering the limitations of the language, it's good enough and can get you quite flexible results (such as comparing multiple fields).

Ascenio avatar Dec 23 '21 01:12 Ascenio