Nikolas Rimikis
1 year ago
committed by
GitHub
12 changed files with 229 additions and 137 deletions
@ -1,58 +1,81 @@
|
||||
// ignore_for_file: public_member_api_docs |
||||
|
||||
/// Signature of a function returning a [Comparable]. |
||||
typedef ComparableGetter<T> = Comparable Function(T); |
||||
|
||||
/// Sorting Box to sort [List]s on multiple properties. |
||||
class SortBox<T extends Enum, R> { |
||||
/// Constructs a new SortBox. |
||||
/// |
||||
/// A *Box* is a record of a property and how to order it. |
||||
SortBox( |
||||
this._properties, |
||||
this._secondaryBoxes, |
||||
this._boxes, |
||||
); |
||||
|
||||
/// A mapping of all values [T] to their [ComparableGetter]. |
||||
final Map<T, ComparableGetter<R>> _properties; |
||||
final Map<T, Box<T>> _secondaryBoxes; |
||||
|
||||
List<R> sort(final List<R> input, final Box<T> box) { |
||||
/// A mapping of values [T] to their *Boxes*. |
||||
/// |
||||
/// The Boxes are applied if two elements are considered equal regarding their property [T]. |
||||
final Map<T, Set<(T property, SortBoxOrder order)>> _boxes; |
||||
|
||||
/// Sorts the [input] list according to their [box]. |
||||
/// |
||||
/// A box contains the property and [SortBoxOrder] how the list should be sorted. |
||||
/// In case the property of two elements is considered equal all following boxes specified at `_boxes[property]` are applied. |
||||
/// If specified [presort] will be applied before [box] and [_boxes]. |
||||
/// |
||||
/// This function sorts the input in place and a reference to it mutating the provided list. |
||||
List<R> sort( |
||||
final List<R> input, |
||||
final (T property, SortBoxOrder order) box, [ |
||||
final Set<(T property, SortBoxOrder order)>? presort, |
||||
]) { |
||||
if (input.length <= 1) { |
||||
return input; |
||||
} |
||||
|
||||
final comparableGetter = _properties[box.property]!; |
||||
final secondaryBox = _secondaryBoxes[box.property]; |
||||
final comparableGetter2 = _properties[secondaryBox?.property]; |
||||
|
||||
return input |
||||
..sort( |
||||
(final item1, final item2) { |
||||
final first = _compare(item1, item2, box.order, comparableGetter); |
||||
final boxes = { |
||||
...?presort, |
||||
box, |
||||
...?_boxes[box.$1], |
||||
}; |
||||
|
||||
if (first == 0 && secondaryBox != null) { |
||||
return _compare(item1, item2, secondaryBox.order, comparableGetter2!); |
||||
} |
||||
final sorted = input..sort((final item1, final item2) => _compare(item1, item2, boxes.iterator..moveNext())); |
||||
|
||||
return first; |
||||
}, |
||||
); |
||||
return sorted; |
||||
} |
||||
|
||||
int _compare(final R item1, final R item2, final SortBoxOrder order, final ComparableGetter<R> getter) { |
||||
final comparable1 = getter(item1); |
||||
final comparable2 = getter(item2); |
||||
int _compare( |
||||
final R item1, |
||||
final R item2, |
||||
final Iterator<(T property, SortBoxOrder order)> iterator, |
||||
) { |
||||
final box = iterator.current; |
||||
final (property, sortBoxOrder) = box; |
||||
final comparableGetter = _properties[property]!; |
||||
|
||||
final comparable1 = comparableGetter(item1); |
||||
final comparable2 = comparableGetter(item2); |
||||
|
||||
final order = switch (sortBoxOrder) { |
||||
SortBoxOrder.ascending => comparable1.compareTo(comparable2), |
||||
SortBoxOrder.descending => comparable2.compareTo(comparable1), |
||||
}; |
||||
|
||||
if (order == 0 && iterator.moveNext()) { |
||||
return _compare(item1, item2, iterator); |
||||
} |
||||
|
||||
return order == SortBoxOrder.ascending ? comparable1.compareTo(comparable2) : comparable2.compareTo(comparable1); |
||||
return order; |
||||
} |
||||
} |
||||
|
||||
/// Sorting order used by [SortBox]. |
||||
enum SortBoxOrder { |
||||
/// Ascending sorting order. |
||||
ascending, |
||||
descending, |
||||
} |
||||
|
||||
class Box<T extends Enum> { |
||||
Box( |
||||
this.property, |
||||
this.order, |
||||
); |
||||
|
||||
final T property; |
||||
final SortBoxOrder order; |
||||
/// Descending sorting order. |
||||
descending, |
||||
} |
||||
|
Loading…
Reference in new issue