Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Iterable.elementAtOrNull extension #217

Merged
merged 6 commits into from Jun 3, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 3 additions & 1 deletion 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
Expand Down
11 changes: 11 additions & 0 deletions lib/src/iterable_extensions.dart
Expand Up @@ -354,6 +354,17 @@ extension IterableExtension<T> on Iterable<T> {
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;
natebosch marked this conversation as resolved.
Show resolved Hide resolved

/// 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]
Expand Down
11 changes: 11 additions & 0 deletions lib/src/list_extensions.dart
Expand Up @@ -259,6 +259,17 @@ extension ListExtensions<E> on List<E> {
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;

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Curious that there is no firstOrNull/lastOrNull defined on List.

/// Contiguous [slice]s of [this] with the given [length].
///
/// Each slice is a view of this list [length] elements long, except for the
Expand Down
2 changes: 1 addition & 1 deletion pubspec.yaml
@@ -1,5 +1,5 @@
name: collection
version: 1.16.1-dev
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's OK to go directly to 1.17.0 and publish.

version: 1.17.0-dev

description: Collections and utilities functions and classes related to collections.
repository: https://github.com/dart-lang/collection
Expand Down
29 changes: 29 additions & 0 deletions test/extensions_test.dart
Expand Up @@ -1160,6 +1160,21 @@ void main() {
}
});
});
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), []);
Expand Down Expand Up @@ -1748,6 +1763,20 @@ void main() {
['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), []);
Expand Down