From 441852cd79db59139661fc48ca003e354180df0c Mon Sep 17 00:00:00 2001 From: Christian Kleineidam Date: Tue, 28 Feb 2023 01:30:29 +0100 Subject: [PATCH 1/3] Create IterableComparableExtensions .min and .max I added .min and max as Iterable extensions. I implemented them to behave the same way with NaN as the current min() and max() functions behave by using the fact that NaN != NaN. --- .../iterable_comparable_extensions.dart | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 sdk/lib/collection/iterable_comparable_extensions.dart diff --git a/sdk/lib/collection/iterable_comparable_extensions.dart b/sdk/lib/collection/iterable_comparable_extensions.dart new file mode 100644 index 000000000000..08db6cc30d70 --- /dev/null +++ b/sdk/lib/collection/iterable_comparable_extensions.dart @@ -0,0 +1,71 @@ +extension IterableComparableExtensions on Iterable { + /* + * Returns the largest value in the iterable determined via + * [Comparable.toCompare()]. + * + * If any value is NaN or otherwise not equal to itself, + * that element is treated as larger than any other. + * + * The iterable must have at least one element, otherwise + * [IterableElementError] gets thrown. + * If it has only one element, that element is returned. + * + * If multiple items are maximal, the function returns the first one + * encountered. + */ + T get max { + final Iterator iterator = this.iterator; + if (!iterator.moveNext()) { + throw IterableElementError.noElement(); + } + T value = iterator.current; + if (value != value) { + return value; + } + + while (iterator.moveNext()) { + final current = iterator.current; + if (current != current) { + return current; + } else if (current.compareTo(value) > 0) { + value = current; + } + } + return value; + } + + /* + * Returns the smallest value in the iterable determined via + * [Comparable.toCompare()]. + * + * If any value is NaN or otherwise not equal to itself, + * that element is treated as smaller than any other. + * + * The iterable must have at least one element, otherwise + * [IterableElementError] gets thrown. + * If it has only one element, that element is returned. + * + * If multiple items are minimal, the function returns the first one + * encountered. + */ + T get min { + final Iterator iterator = this.iterator; + if (!iterator.moveNext()) { + throw IterableElementError.noElement(); + } + T value = iterator.current; + if (value != value) { + return value; + } + + while (iterator.moveNext()) { + final current = iterator.current; + if (current != current) { + return current; + } else if (current.compareTo(value) < 0) { + value = current; + } + } + return value; + } +} From 3be8752af4cd7e3f3558cfe8ca22bfed08dc335b Mon Sep 17 00:00:00 2001 From: Christian Kleineidam Date: Tue, 28 Feb 2023 01:37:38 +0100 Subject: [PATCH 2/3] Added part of dart.collection; --- sdk/lib/collection/iterable_comparable_extensions.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sdk/lib/collection/iterable_comparable_extensions.dart b/sdk/lib/collection/iterable_comparable_extensions.dart index 08db6cc30d70..4b752d2d215c 100644 --- a/sdk/lib/collection/iterable_comparable_extensions.dart +++ b/sdk/lib/collection/iterable_comparable_extensions.dart @@ -1,3 +1,5 @@ +part of dart.collection; + extension IterableComparableExtensions on Iterable { /* * Returns the largest value in the iterable determined via From 198c361ee2dd4cb5ea730517ed9df50df55ed9c8 Mon Sep 17 00:00:00 2001 From: Christian Kleineidam Date: Tue, 28 Feb 2023 01:52:54 +0100 Subject: [PATCH 3/3] Added license info --- .../iterable_comparable_extensions.dart | 112 ++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 tests/lib/collection/iterable_comparable_extensions.dart diff --git a/tests/lib/collection/iterable_comparable_extensions.dart b/tests/lib/collection/iterable_comparable_extensions.dart new file mode 100644 index 000000000000..7cdff5d86104 --- /dev/null +++ b/tests/lib/collection/iterable_comparable_extensions.dart @@ -0,0 +1,112 @@ +// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import "dart:collection"; +import 'package:test/test.dart'; + +class _AlwaysEqual extends Comparable { + late final int value; + + @override + int compareTo(other) { + return 0; + } + + _AlwaysEqual(int value) { + this.value = value; + } +} + +class _ValueContainer extends Comparable<_ValueContainer> { + late int value; + + _ValueContainer(int value) { + this.value = value; + } + + @override + int compareTo(other) { + return this.value - other.value; + } +} + +class _UnequalToItself extends _ValueContainer { + _UnequalToItself(int value) : super(value); + + operator ==(Object value) { + return false; + } + + @override + int get hashCode => super.hashCode; +} + +void main() { + group("Testing whether the Iterable extension .max works", () { + test("Gives the highest number out of normal numbers", () { + expect([1, 2, 3].max, (equals(3))); + expect( + [_ValueContainer(3), _ValueContainer(2), _ValueContainer(1)] + .max + .value, + (equals(3))); + expect([-3, 2, 1].max, (equals(2))); + }); + + test("Uses the compare function", () { + expect([_AlwaysEqual(10), _AlwaysEqual(1)].max.value, (equals(10))); + }); + + test("Treats -0.0 as bigger than 0.0", () { + expect([-0.0, 0.0].max, (equals(0.0))); + expect([0.0, -0.0].max, (equals(0.0))); + }); + + test( + 'Treats NaN and other values that are unequal to itself ' + 'as lower than other numbers', () { + expect([double.minPositive, double.maxFinite * -1, double.nan].max.isNaN, + (equals(true))); + expect( + [_ValueContainer(1), _UnequalToItself(0), _UnequalToItself(-1)] + .max + .value, + (equals(0))); + }); + }); + + group("Testing whether the Iterable extension .min works", () { + test("Returns the lowest number out of normal numbers", () { + expect([1, 2, 3].min, (equals(1))); + expect( + [_ValueContainer(3), _ValueContainer(2), _ValueContainer(1)] + .min + .value, + (equals(1))); + int value = [-3, 2, 1].min; + expect(value, (equals(-3))); + }); + + test("Uses the compare function", () { + expect([_AlwaysEqual(10), _AlwaysEqual(1)].max.value, (equals(10))); + }); + + test("Treats -0.0 as smaller than 0.0", () { + expect([-0.0, 0.0].min.isNegative, (equals(true))); + expect([0.0, -0.0].min.isNegative, (equals(true))); + }); + + test( + 'Treats NaN and other values that are unequal to itself ' + 'as lower than other numbers', () { + expect([double.minPositive, double.maxFinite * -1, double.nan].min.isNaN, + (equals(true))); + expect( + [_ValueContainer(1), _UnequalToItself(0), _UnequalToItself(-1)] + .max + .value, + (equals(0))); + }); + }); +}