I have a List<List<Object>>>
. What is the best readable way to unique my list?
For instance:
[[A, B], [B, A], [B, C, B]]
to:
[[A, B], [B, C, B]]
CodePudding user response:
If you are alright with modelling the result as a Set<Set<Object>>
, I would suggest this approach.
I am using the collection package because it provides an easy way to check if two collections are equal.
import 'dart:collection';
import 'package:collection/collection.dart';
void main() {
List<List<String>> items = [
['A', 'B'],
['B', 'A'],
['B', 'C'],
];
Set<Set<String>> unique = HashSet<Set<String>>(
equals: SetEquality().equals,
hashCode: SetEquality().hash,
);
unique.addAll(items.map((v) => v.toSet()));
print(items);
print(unique);
}
output
[[A, B], [B, A], [B, C]]
{{A, B}, {B, C}}
Since the inner lists
- are arbitrary length
- can contain duplicates
- are not necessarily in the same order (but we want to treat different orderings as the same list)
It does make things more complicated, but you can use the same general approach. Here we will keep the inner elements as lists, but we will provide definitions for equals and hashCode that take the above constraints into account.
If the elements implement Comparable then you can use .sorted()
to account for constraint #3, and ListEquality().equals
and ListEquality().hash
to account for constraints #1 and #2.
import 'dart:collection';
import 'package:collection/collection.dart';
void main() {
List<List<String>> items = [
['A', 'B'],
['B', 'A'],
['B', 'C', 'B'],
];
Set<List<String>> unique = HashSet<List<String>>(
equals: (a, b) => ListEquality().equals(a.sorted(), b.sorted()),
hashCode: (a) => ListEquality().hash(a.sorted()),
);
unique.addAll(items);
print(items);
print(unique);
}
output
[[A, B], [B, A], [B, C, B]]
{[A, B], [B, C, B]}
However, what if the elements don't implement Comparable
?
You have a few options in this case.
First, the .sorted()
method optionally accepts a function that you can use to provide custom sorting logic.
The other approach would be to get a count of occurrences of each element in the list and compare the counts. I have implemented this approach below.
import 'dart:collection';
import 'package:collection/collection.dart';
void main() {
List<List<String>> items = [
['A', 'B'],
['B', 'A'],
['B', 'C', 'B'],
];
Set<List<String>> unique = HashSet<List<String>>(
equals: (a, b) => MapEquality().equals(counts(a), counts(b)),
hashCode: (a) => MapEquality().hash(counts(a)),
);
unique.addAll(items);
print(items);
print(unique);
}
Map<T, int> counts<T>(List<T> items) {
Map<T, int> result = {};
for (final item in items) {
result.update(item, (v) => v 1, ifAbsent: () => 1);
}
return result;
}
output
[[A, B], [B, A], [B, C, B]]
{[B, C, B], [A, B]}
Note that the elements are in a different order than the previous solution, this is because HashSet
does not preserve the insertion order, if you do want to preserve the order you can use a LinkedHashSet
instead.