Skip to content

Commit

Permalink
Add minMax operator on iterable and comparators.
Browse files Browse the repository at this point in the history
  • Loading branch information
renggli committed Dec 20, 2023
1 parent 4b7d64d commit 4dde933
Show file tree
Hide file tree
Showing 7 changed files with 124 additions and 78 deletions.
3 changes: 1 addition & 2 deletions lib/comparator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@ export 'src/comparator/modifiers/result_of.dart';
export 'src/comparator/modifiers/reversed.dart';
export 'src/comparator/operations/binary_search.dart';
export 'src/comparator/operations/largest.dart';
export 'src/comparator/operations/max.dart';
export 'src/comparator/operations/min.dart';
export 'src/comparator/operations/min_max.dart';
export 'src/comparator/operations/ordered.dart';
export 'src/comparator/operations/predicates.dart';
export 'src/comparator/operations/smallest.dart';
Expand Down
42 changes: 29 additions & 13 deletions lib/src/collection/iterable/operators.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,6 @@ extension OperatorsIterableExtension<E> on Iterable<E> {
E min({Comparator<E>? comparator, E Function()? orElse}) =>
(comparator ?? naturalCompare).minOf(this, orElse: orElse);

/// Returns a list of the [count] smallest elements of this [Iterable]. The
/// elements need to be [Comparable], unless a custom [comparator] is
/// provided.
///
/// For example
///
/// [3, 1, 2].smallest(2)
///
/// returns `[1, 2]`.
///
List<E> smallest(int count, {Comparator<E>? comparator}) =>
(comparator ?? naturalCompare).smallest(this, count);

/// Returns the maximum of this [Iterable]. The elements need to be
/// [Comparable], unless a custom [comparator] is provided.
///
Expand All @@ -44,6 +31,35 @@ extension OperatorsIterableExtension<E> on Iterable<E> {
E max({Comparator<E>? comparator, E Function()? orElse}) =>
(comparator ?? naturalCompare).maxOf(this, orElse: orElse);

/// Returns the minimum and maximum of this [Iterable] at once. The elements
/// need to be [Comparable], unless a custom [comparator] is provided.
///
/// Throws a [StateError] if the iterable is empty, unless an [orElse]
/// function is provided.
///
/// For example
///
/// [3, 1, 2].minMax()
///
/// returns `(min: 1, max: 3)`.
///
({E min, E max}) minMax(
{Comparator<E>? comparator, ({E min, E max}) Function()? orElse}) =>
(comparator ?? naturalCompare).minMaxOf(this, orElse: orElse);

/// Returns a list of the [count] smallest elements of this [Iterable]. The
/// elements need to be [Comparable], unless a custom [comparator] is
/// provided.
///
/// For example
///
/// [3, 1, 2].smallest(2)
///
/// returns `[1, 2]`.
///
List<E> smallest(int count, {Comparator<E>? comparator}) =>
(comparator ?? naturalCompare).smallest(this, count);

/// Returns a list of the [count] largest elements of this [Iterable]. The
/// elements need to be [Comparable], unless a custom [comparator] is
/// provided.
Expand Down
20 changes: 0 additions & 20 deletions lib/src/comparator/operations/max.dart

This file was deleted.

20 changes: 0 additions & 20 deletions lib/src/comparator/operations/min.dart

This file was deleted.

32 changes: 32 additions & 0 deletions lib/src/comparator/operations/min_max.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
extension MinMaxComparator<T> on Comparator<T> {
/// Returns the minimum of the two arguments [a] and [b].
T min(T a, T b) => this(a, b) < 0 ? a : b;

/// Returns the maximum of the provided [iterable].
T minOf(Iterable<T> iterable, {T Function()? orElse}) =>
orElse != null && iterable.isEmpty ? orElse() : iterable.reduce(min);

/// Returns the maximum of the two arguments [a] and [b].
T max(T a, T b) => this(a, b) > 0 ? a : b;

/// Returns the maximum of the provided [iterable].
T maxOf(Iterable<T> iterable, {T Function()? orElse}) =>
orElse != null && iterable.isEmpty ? orElse() : iterable.reduce(max);

/// Returns a tuple with the minimum and maximum of the provided [iterable].
({T min, T max}) minMaxOf(Iterable<T> iterable,
{({T min, T max}) Function()? orElse}) {
final iterator = iterable.iterator;
if (iterator.moveNext()) {
var minValue = iterator.current;
var maxValue = iterator.current;
while (iterator.moveNext()) {
minValue = min(minValue, iterator.current);
maxValue = max(maxValue, iterator.current);
}
return (min: minValue, max: maxValue);
}
if (orElse == null) throw StateError("No element");
return orElse();
}
}
15 changes: 15 additions & 0 deletions test/collection_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1062,6 +1062,21 @@ void main() {
expect([3, 2, 1].max(comparator: reverse), 1);
});
});
group('min/max', () {
const sentinel = (min: -1, max: -1);
test('empty', () {
expect(() => empty.minMax(), throwsStateError);
expect(empty.minMax(orElse: () => sentinel), sentinel);
});
test('comparable', () {
expect([1, 2, 3].minMax(), (min: 1, max: 3));
expect([3, 2, 1].minMax(), (min: 1, max: 3));
});
test('custom comparator', () {
expect([1, 2, 3].minMax(comparator: reverse), (min: 3, max: 1));
expect([3, 2, 1].minMax(comparator: reverse), (min: 3, max: 1));
});
});
group('smallest', () {
test('empty', () {
expect(empty.smallest(0), isEmpty);
Expand Down
70 changes: 47 additions & 23 deletions test/comparator_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,32 @@ void main() {
expect(naturalInt.largest([2, 3, 1], 5), [3, 2, 1]);
expect(naturalInt.largest([2, 3, 1, 5, 4], 5), [5, 4, 3, 2, 1]);
});
group('min', () {
test('min', () {
expect(naturalInt.min(1, 1), 1);
expect(naturalInt.min(1, 2), 1);
expect(naturalInt.min(2, 1), 1);
expect(naturalInt.min(2, 2), 2);
});
test('minOf', () {
expect(() => naturalInt.minOf([]), throwsStateError);
expect(naturalInt.minOf([1]), 1);
expect(naturalInt.minOf([1, 2]), 1);
expect(naturalInt.minOf([2, 1]), 1);
expect(naturalInt.minOf([1, 2, 3]), 1);
expect(naturalInt.minOf([1, 3, 2]), 1);
expect(naturalInt.minOf([2, 1, 3]), 1);
expect(naturalInt.minOf([2, 3, 1]), 1);
expect(naturalInt.minOf([3, 1, 2]), 1);
expect(naturalInt.minOf([3, 2, 1]), 1);
});
test('minOf orElse', () {
expect(naturalInt.minOf([], orElse: () => -1), -1);
expect(naturalInt.minOf([1], orElse: () => -1), 1);
expect(naturalInt.minOf([1, 2], orElse: () => -1), 1);
expect(naturalInt.minOf([1, 2, 3], orElse: () => -1), 1);
});
});
group('max', () {
test('max', () {
expect(naturalInt.max(1, 1), 1);
Expand All @@ -417,30 +443,28 @@ void main() {
expect(naturalInt.maxOf([1, 2, 3], orElse: () => -1), 3);
});
});
group('min', () {
test('min', () {
expect(naturalInt.min(1, 1), 1);
expect(naturalInt.min(1, 2), 1);
expect(naturalInt.min(2, 1), 1);
expect(naturalInt.min(2, 2), 2);
});
test('minOf', () {
expect(() => naturalInt.minOf([]), throwsStateError);
expect(naturalInt.minOf([1]), 1);
expect(naturalInt.minOf([1, 2]), 1);
expect(naturalInt.minOf([2, 1]), 1);
expect(naturalInt.minOf([1, 2, 3]), 1);
expect(naturalInt.minOf([1, 3, 2]), 1);
expect(naturalInt.minOf([2, 1, 3]), 1);
expect(naturalInt.minOf([2, 3, 1]), 1);
expect(naturalInt.minOf([3, 1, 2]), 1);
expect(naturalInt.minOf([3, 2, 1]), 1);
group('min & max', () {
test('minMaxOf', () {
expect(() => naturalInt.maxOf([]), throwsStateError);
expect(naturalInt.minMaxOf([1]), (min: 1, max: 1));
expect(naturalInt.minMaxOf([1, 2]), (min: 1, max: 2));
expect(naturalInt.minMaxOf([2, 1]), (min: 1, max: 2));
expect(naturalInt.minMaxOf([1, 2, 3]), (min: 1, max: 3));
expect(naturalInt.minMaxOf([1, 3, 2]), (min: 1, max: 3));
expect(naturalInt.minMaxOf([2, 1, 3]), (min: 1, max: 3));
expect(naturalInt.minMaxOf([2, 3, 1]), (min: 1, max: 3));
expect(naturalInt.minMaxOf([3, 1, 2]), (min: 1, max: 3));
expect(naturalInt.minMaxOf([3, 2, 1]), (min: 1, max: 3));
});
test('minOf orElse', () {
expect(naturalInt.minOf([], orElse: () => -1), -1);
expect(naturalInt.minOf([1], orElse: () => -1), 1);
expect(naturalInt.minOf([1, 2], orElse: () => -1), 1);
expect(naturalInt.minOf([1, 2, 3], orElse: () => -1), 1);
test('minMaxOf orElse', () {
const sentinel = (min: -1, max: -1);
expect(naturalInt.minMaxOf([], orElse: () => sentinel), sentinel);
expect(
naturalInt.minMaxOf([1], orElse: () => sentinel), (min: 1, max: 1));
expect(naturalInt.minMaxOf([1, 2], orElse: () => sentinel),
(min: 1, max: 2));
expect(naturalInt.minMaxOf([1, 2, 3], orElse: () => sentinel),
(min: 1, max: 3));
});
});
group('ordered', () {
Expand Down

0 comments on commit 4dde933

Please sign in to comment.