dart-lang / collection

The collection package for Dart contains a number of separate libraries with utility functions and classes that makes working with collections easier.
https://pub.dev/packages/collection
BSD 3-Clause "New" or "Revised" License
373 stars 87 forks source link

Equality subclass which is conjunction of other Equalities #14

Open seaneagan opened 9 years ago

seaneagan commented 9 years ago

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);
}
kendalharland commented 7 years ago

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);
zoechi commented 7 years ago

@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

matanlurey commented 6 years ago

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

nex3 commented 6 years ago

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

lrhn commented 4 years ago

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;
}