- Added List.equals extension
This commit is contained in:
Hans Kokx
2025-04-15 15:26:43 +02:00
parent c8c30d3838
commit 5b402763d1
5 changed files with 304 additions and 6 deletions
+112 -3
View File
@@ -37,10 +37,10 @@ extension Unique<E, Id> on List<E> {
/// print(uniquePeople.map((p) => p.name)); // Output: ['Alice', 'Bob']
/// ```
///
List<E> unique([Id Function(E element)? id, bool inplace = true]) {
final Set ids = {};
List<E> unique([Object? Function(E element)? id, bool inplace = true]) {
final Set<Object?> ids = {};
final List<E> list = inplace ? this : List<E>.from(this);
list.retainWhere((x) => ids.add(id != null ? id(x) : x as Id));
list.retainWhere((x) => ids.add(id != null ? id(x) : x));
return list;
}
}
@@ -122,3 +122,112 @@ extension ListNullability on List? {
/// This is identical to [isEmptyOrNull].
bool get isEmptyOrNull => isNullOrEmpty;
}
/// Extension on nullable lists of nullable elements to provide a custom equality check.
extension ListEquality<T> on List<T?>? {
/// Checks if this list is equal to another list.
///
/// Two lists are considered equal if:
/// - Both are null, or
/// - Both have the same length, and
/// - Elements at the same index are equal.
///
/// Nullable list elements are handled as follows:
/// - If both elements at a given index are null, they are considered equal.
/// - If one element is null and the other is not, they are considered unequal.
///
/// The type parameter `T` represents the type of elements in the list.
/// The elements can be nullable (`T?`).
///
/// Example:
///
/// ```dart
/// List<int?>? list1 = [1, 2, null, 4];
/// List<int?>? list2 = [1, 2, null, 4];
/// List<int?>? list3 = [1, 2, 3, 4];
/// List<int?>? list4 = null;
/// List<int?>? list5 = [1, 2, 3, null];
///
/// print(list1.equals(list2)); // Output: true
/// print(list1.equals(list3)); // Output: false
/// print(list1.equals(list4)); // Output: false
/// print(list4.equals(null)); // Output: true
/// print(list5.equals([1,2,3,null])); //Output: true
///
/// // Example with ignoreSorting:
/// List<int>? list6 = [1, 2, 3];
/// List<int>? list7 = [3, 1, 2];
/// print(list6.equals(list7, ignoreSorting: true)); // Output: true (order doesn't matter)
/// print(list6.equals(list7, ignoreSorting: false)); // Output: false (order matters)
///
/// List<String>? list8 = ["apple", "banana", "cherry"];
/// List<String>? list9 = ["cherry", "apple", "banana"];
/// print(list8.equals(list9, ignoreSorting: true)); // Output: true
/// print(list8.equals(list9, ignoreSorting: false)); // Output: false
/// ```
///
/// Returns `true` if the lists are equal, `false` otherwise.
///
/// The [ignoreSorting] parameter, if set to true, will compare the lists
/// after sorting them. This defaults to false.
bool equals(
List<T?>? a, {
bool ignoreSorting = false,
}) {
if (this == null) return a == null;
if (a == null || this?.length != a.length) return false;
if (this.runtimeType != a.runtimeType) return false;
if (ignoreSorting) {
// Create copies to avoid modifying the original lists.
final List<T?> sortedThis = this?.toList() ?? [];
final List<T?> sortedA = a.toList();
// If the lists contain non-comparable elements, we can't rely on sorting to determine equality.
if (T is! Comparable) {
// Instead, we check if the lists contain the same elements, regardless of order.
// This is an O(n^2) operation, but it's the only reliable way to compare non-comparable lists.
for (final itemThis in sortedThis) {
final int indexA = sortedA.indexOf(itemThis);
if (indexA == -1) {
// itemThis is not in sortedA, so the lists are not equal.
return false;
}
// Remove the item from sortedA to avoid double-counting.
sortedA.removeAt(indexA);
}
// If sortedA is empty, all elements in sortedThis were found in sortedA.
return sortedA.isEmpty;
}
// Sort if comparable
sortedThis.sort((a, b) {
if (a == null && b == null) return 0;
if (a == null) return -1;
if (b == null) return 1;
return (a as Comparable).compareTo(b as Comparable);
});
sortedA.sort((a, b) {
if (a == null && b == null) return 0;
if (a == null) return -1;
if (b == null) return 1;
return (a as Comparable).compareTo(b as Comparable);
});
// Compare sorted lists
for (int i = 0; i < sortedThis.length; i++) {
if (sortedThis[i] != sortedA[i]) {
return false;
}
}
return true;
} else {
// Original comparison
for (int i = 0; i < (this?.length ?? 0); i++) {
if (this?[i] != a[i]) {
return false;
}
}
return true;
}
}
}