diff --git a/CHANGELOG.md b/CHANGELOG.md index c04cc493..93cd5e18 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 2.0.2 + +- fix: `Map` prop with non-comparable key + # 2.0.1 - fix: `hashCode` should be the same for equal objects (`Map` fix) diff --git a/lib/src/equatable_utils.dart b/lib/src/equatable_utils.dart index 50dbb2ba..4a18c5b7 100644 --- a/lib/src/equatable_utils.dart +++ b/lib/src/equatable_utils.dart @@ -1,5 +1,3 @@ -import 'dart:collection'; - import 'package:collection/collection.dart'; import 'package:equatable/equatable.dart'; @@ -41,11 +39,11 @@ bool _isEquatable(dynamic object) { /// https://en.wikipedia.org/wiki/Jenkins_hash_function int _combine(int hash, dynamic object) { if (object is Map) { - SplayTreeMap.of(object).forEach( - (dynamic key, dynamic value) { - hash = hash ^ _combine(hash, [key, value]); - }, - ); + object.keys + .sorted((dynamic a, dynamic b) => a.hashCode - b.hashCode) + .forEach((dynamic key) { + hash = hash ^ _combine(hash, [key, object[key]]); + }); return hash; } if (object is Iterable) { diff --git a/pubspec.yaml b/pubspec.yaml index a9fe7e75..ee845ad9 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: equatable description: A Dart package that helps to implement value based equality without needing to explicitly override == and hashCode. -version: 2.0.1 +version: 2.0.2 repository: https://github.com/felangel/equatable issue_tracker: https://github.com/felangel/equatable/issues homepage: https://github.com/felangel/equatable diff --git a/test/equatable_test.dart b/test/equatable_test.dart index 3809e0dc..5cc93ea1 100644 --- a/test/equatable_test.dart +++ b/test/equatable_test.dart @@ -363,6 +363,109 @@ void main() { }); }); + group('Simple Equatable (map custom key)', () { + test('should correct toString', () { + final instance = SimpleEquatable(, dynamic>{}); + expect( + instance.toString(), + 'SimpleEquatable, dynamic>>({})', + ); + }); + + test('should return true when instance is the same', () { + final instance = SimpleEquatable( + { + SimpleEquatable('a'): 1, + SimpleEquatable('b'): 2, + SimpleEquatable('c'): 3 + }, + ); + expect(instance == instance, true); + }); + + test('should return correct hashCode', () { + final instance = SimpleEquatable( + { + SimpleEquatable('a'): 1, + SimpleEquatable('b'): 2, + SimpleEquatable('c'): 3 + }, + ); + expect( + instance.hashCode, + instance.runtimeType.hashCode ^ mapPropsToHashCode(instance.props), + ); + }); + + test('should have same hashCode when values are equal', () { + final instanceA = SimpleEquatable( + { + SimpleEquatable('a'): 1, + SimpleEquatable('b'): 2, + SimpleEquatable('c'): 3 + }, + ); + final instanceB = SimpleEquatable( + { + SimpleEquatable('b'): 2, + SimpleEquatable('a'): 1, + SimpleEquatable('c'): 3 + }, + ); + expect(instanceA == instanceB, true); + expect(instanceA.hashCode, instanceB.hashCode); + }); + + test('should return true when instances are different', () { + final instanceA = SimpleEquatable( + { + SimpleEquatable('a'): 1, + SimpleEquatable('b'): 2, + SimpleEquatable('c'): 3 + }, + ); + final instanceB = SimpleEquatable( + { + SimpleEquatable('a'): 1, + SimpleEquatable('b'): 2, + SimpleEquatable('c'): 3 + }, + ); + expect(instanceA == instanceB, true); + expect(instanceA.hashCode == instanceB.hashCode, true); + }); + + test('should return false when compared to non-equatable', () { + final instanceA = SimpleEquatable( + { + SimpleEquatable('a'): 1, + SimpleEquatable('b'): 2, + SimpleEquatable('c'): 3 + }, + ); + final instanceB = NonEquatable(); + expect(instanceA == instanceB, false); + }); + + test('should return false when values are different', () { + final instanceA = SimpleEquatable( + { + SimpleEquatable('a'): 1, + SimpleEquatable('b'): 2, + SimpleEquatable('c'): 3 + }, + ); + final instanceB = SimpleEquatable( + { + SimpleEquatable('a'): 1, + SimpleEquatable('b'): 2, + SimpleEquatable('c'): 2 + }, + ); + expect(instanceA == instanceB, false); + }); + }); + group('Simple Equatable (Equatable)', () { test('should correct toString', () { final instance = SimpleEquatable(EquatableData(