Add elementAtOrNull extensions (#217)
Add extensions on List and Iterable to read an element without risk of
exceptions.
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f36e987..ea92e11 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,6 @@
-## 1.16.1-dev
+## 1.17.0-dev
+
+* Add `Iterable.elementAtOrNull` and `List.elementAtOrNull` extension methods.
* Add a top-level `lastBy()` function that converts an `Iterable` to a `Map` by
grouping its elements using a function, keeping the last element for each
diff --git a/lib/src/iterable_extensions.dart b/lib/src/iterable_extensions.dart
index 88f320d..3af5124 100644
--- a/lib/src/iterable_extensions.dart
+++ b/lib/src/iterable_extensions.dart
@@ -354,6 +354,17 @@
return null;
}
+ /// The [index]th element, or `null` if there is no such element.
+ ///
+ /// Returns the element at position [index] of this iterable,
+ /// just like [elementAt], if this iterable has such an element.
+ /// If this iterable does not have enough elements to have one with the given
+ /// [index], the `null` value is returned, unlike [elementAt] which throws
+ /// instead.
+ ///
+ /// The [index] must not be negative.
+ T? elementAtOrNull(int index) => skip(index).firstOrNull;
+
/// Associates the elements in [this] by the value returned by [key].
///
/// Returns a map from keys computed by [key] to the last value for which [key]
diff --git a/lib/src/list_extensions.dart b/lib/src/list_extensions.dart
index 4d6ae3f..4bb719a 100644
--- a/lib/src/list_extensions.dart
+++ b/lib/src/list_extensions.dart
@@ -259,6 +259,17 @@
return true;
}
+ /// The [index]th element, or `null` if there is no such element.
+ ///
+ /// Returns the element at position [index] of this list,
+ /// just like [elementAt], if this list has such an element.
+ /// If this list does not have enough elements to have one with the given
+ /// [index], the `null` value is returned, unlike [elementAt] which throws
+ /// instead.
+ ///
+ /// The [index] must not be negative.
+ E? elementAtOrNull(int index) => (index < length) ? this[index] : null;
+
/// Contiguous [slice]s of [this] with the given [length].
///
/// Each slice is a view of this list [length] elements long, except for the
diff --git a/pubspec.yaml b/pubspec.yaml
index 436a1e1..07a5fa1 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,5 +1,5 @@
name: collection
-version: 1.16.1-dev
+version: 1.17.0-dev
description: Collections and utilities functions and classes related to collections.
repository: https://github.com/dart-lang/collection
diff --git a/test/extensions_test.dart b/test/extensions_test.dart
index ce4b2e9..31bb458 100644
--- a/test/extensions_test.dart
+++ b/test/extensions_test.dart
@@ -1160,6 +1160,21 @@
}
});
});
+ group('.elementAtOrNull', () {
+ test('empty', () async {
+ expect(iterable([]).elementAtOrNull(0), isNull);
+ });
+ test('negative index', () async {
+ expect(() => iterable([1]).elementAtOrNull(-1),
+ throwsA(isA<RangeError>()));
+ });
+ test('index within range', () async {
+ expect(iterable([1]).elementAtOrNull(0), 1);
+ });
+ test('index too high', () async {
+ expect(iterable([1]).elementAtOrNull(1), isNull);
+ });
+ });
group('.slices', () {
test('empty', () {
expect(iterable(<int>[]).slices(1), []);
@@ -1748,6 +1763,20 @@
['1', 'b']);
});
});
+ group('.elementAtOrNull', () {
+ test('empty', () async {
+ expect([].elementAtOrNull(0), isNull);
+ });
+ test('negative index', () async {
+ expect(() => [1].elementAtOrNull(-1), throwsA(isA<RangeError>()));
+ });
+ test('index within range', () async {
+ expect([1].elementAtOrNull(0), 1);
+ });
+ test('index too high', () async {
+ expect([1].elementAtOrNull(1), isNull);
+ });
+ });
group('.slices', () {
test('empty', () {
expect(<int>[].slices(1), []);