collection icon indicating copy to clipboard operation
collection copied to clipboard

Equality subclass which is conjunction of other Equalities

Open seaneagan opened this issue 9 years ago • 5 comments

Currently I do something like:

  bool operator ==(other) =>
      other is TaskInvocation &&
      name == other.name &&
      const IterableEquality().equals(positionals, other.positionals) &&
      const MapEquality().equals(options, other.options);

  int get hashCode =>
      name == other.name.hashCode ^
      (const IterableEquality().hash(positionals) * 3) ^
      (const MapEquality().hash(options) * 5);

which has lots of duplication between == and hashCode. I'd prefer to have something like:

class AndEquality<E> {
  /// Equality for which elements are equal if equal according to all in [equalities].
  const AndEquality<E>(Iterable<Equality<E>> equalities);
}

seaneagan avatar Jun 19 '15 22:06 seaneagan

Why not use hashCode in your == definition?

    bool operator ==(other) =>
        other is TaskInvocation && hashCode == other.hashCode;

    int get hashCode =>
        name.hashCode ^
        (const IterableEquality().hash(positionals) * 3) ^
        (const MapEquality().hash(options) * 5);

kendalharland avatar Jan 06 '17 00:01 kendalharland

@kharland hashCode is required to be equal for instances that are considered equal, but hashCode is not required to be different for instances that are considered not equal

zoechi avatar Jan 08 '17 16:01 zoechi

This has been open since 2015 without action so I am closing as stale.

matanlurey avatar Mar 12 '18 16:03 matanlurey

Re-opening as per https://github.com/dart-lang/collection/issues/6#issuecomment-372500079.

nex3 avatar Mar 15 '18 18:03 nex3

Basically, what is needed is a way to create an equality for a type by extracting properties from the type and assigning an equality to each.

Either operation might be useful by itself, so:

class PropertyEquality<T, S> implements Equality<T>{
  final S Function(T) _propertyValue;
  final Equality<S> propertyEquality;
  PropertyEquality(S Function(T) propertyValue, this.propertyEquality): _propertyValue = propertyValue;
  S propertyValue(T object) => _propertyValue(object);
  bool equals(T object1,  T object2) => 
    propertyEquality.equals(_propertyValue(object1), _propertyValue(object2));
  int hash(T object) => propertyEquality.hash(_propertyValue(object));
  bool isValidKey(Object object) => object is T;
}

class CompositeEquality<T> implements Equality<T> {
  final List<Equality<T>> _equalities;
  CompositeEquality(Iterable<Equality<T>> equalities) : _equalities = [...equalities];
  bool equals(T object1, T object2) {
    for (var eq in _equalities) if (!eq.equals(object1, object2)) return false;
    return true;
  }
  int hash(T object) {
    int hash = 0;
    for (var eq in _equalities) { 
      // Or some other way to combine hashes.
      hash = hash * 37;
      hash ^= eq.hash(object)
      hash &= 0x3FFFFFFF;
    }
    return hash;
  }
  bool isValidKey(Object object) => object is T;
}

lrhn avatar Sep 14 '20 12:09 lrhn