Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions pkgs/collection/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 1.19.1-wip
- Add `IterableMapEntryExtension` for working on `Map` as a list of pairs, using
`Map.entries`.

## 1.19.1

- Move to `dart-lang/core` monorepo.
Expand Down
27 changes: 27 additions & 0 deletions pkgs/collection/lib/src/iterable_extensions.dart
Original file line number Diff line number Diff line change
Expand Up @@ -914,6 +914,33 @@ extension IterableIterableExtension<T> on Iterable<Iterable<T>> {
};
}

/// Extension on iterables of [MapEntry].
///
/// An [Iterable<MapEntry>] is obtained using [Map.entries], these extensions
/// make it easy to work on a [Map] as a list of pairs.
extension IterableMapEntryExtension<K, V> on Iterable<MapEntry<K, V>> {
/// Creates a new lazy [Iterable] with all elements whose [MapEntry.key]
/// satisfy the predicate [test].
Iterable<MapEntry<K, V>> whereKey(bool Function(K) test) =>
where((e) => test(e.key));

/// Creates a new lazy [Iterable] with all elements whose [MapEntry.value]
/// satisfy the predicate [test].
Iterable<MapEntry<K, V>> whereValue(bool Function(V) test) =>
where((e) => test(e.value));
Copy link
Member

Choose a reason for hiding this comment

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

(Too bad where is taken, so it can't be where({bool Function(K)? key, bool Function(V)? value}))

Copy link
Member Author

@jonasfj jonasfj Nov 5, 2024

Choose a reason for hiding this comment

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

Yes, I feel the same with .map.

But I wouldn't make where({bool Function(K)? key, bool Function(V)? value}).
I would make: where(bool Function(K, V) test), ignoring a parameter in a closure is not that bad.

Maybe, we should consider adding:

  • wherePair(bool Function(K, V) test)
  • mapPair<T>(T Function(K, V) toElement)

We could consider adding such extension methods to:

  • Iterable<MapEntry<K, V>>, and,
  • Iterable<(A, B)>.

But that seems outside the scope of this PR.

And it's entirely possible that a language feature providing pattern matching in the signature of a closure is better.

Copy link
Member

@lrhn lrhn Nov 6, 2024

Choose a reason for hiding this comment

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

Maybe whereEntry/mapEntry/anyEntry/everyEntry with an X Function(K, V) argument. (Don't say "pair", it's not — just — a pair! It's a key-and-value.)
Some of those (not whereEntry) could be extensions to Map<K,V> too. (Not saying they should, but they could.)


/// Create an new lazy [Iterable] with [MapEntry.key] from all elements.
Iterable<K> get keys => map((e) => e.key);

/// Create an new lazy [Iterable] with [MapEntry.value] from all elements.
Iterable<V> get values => map((e) => e.value);

/// Create a [Map] from all elements.
///
/// This is a short-hand for [Map.fromEntries].
Map<K, V> toMap() => Map.fromEntries(this);
}

/// Extensions that apply to iterables of [Comparable] elements.
///
/// These operations can assume that the elements have a natural ordering,
Expand Down
2 changes: 1 addition & 1 deletion pkgs/collection/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: collection
version: 1.19.1
version: 1.19.1-wip
Copy link
Member

Choose a reason for hiding this comment

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

Should be 1.20.0, unless 1.19.0 hasn't been released yet.
(Adds new feature ⇒ minor version increment.)

description: >-
Collections and utilities functions and classes related to collections.
repository: https://github.com/dart-lang/core/tree/main/pkgs/collection
Expand Down
81 changes: 81 additions & 0 deletions pkgs/collection/test/extensions_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1122,6 +1122,87 @@ void main() {
});
});
});
group('of MapEntry', () {
group('.whereKey', () {
test('empty', () {
expect(<String, int>{}.entries.whereKey(unreachable), isEmpty);
});
test('single', () {
expect({'a': 1}.entries.whereKey((k) => k == 'a').toMap(), {'a': 1});
expect({'a': 1}.entries.whereKey((k) => k == 'b').toMap(), isEmpty);
});
test('multiple', () {
expect(
{'a': 1, 'b': 2}.entries.whereKey((k) => k == 'a').toMap(),
{'a': 1},
);
expect(
{'a': 1, 'b': 2}.entries.whereKey((k) => k == 'b').toMap(),
{'b': 2},
);
expect(
{'a': 1, 'b': 2}.entries.whereKey((k) => k != 'c').toMap(),
{'a': 1, 'b': 2},
);
});
});
group('.whereValue', () {
test('empty', () {
expect(<String, int>{}.entries.whereValue(unreachable), isEmpty);
});
test('single', () {
expect({'a': 1}.entries.whereValue((v) => v == 1).toMap(), {'a': 1});
expect({'a': 1}.entries.whereValue((v) => v == 2).toMap(), isEmpty);
});
test('multiple', () {
expect(
{'a': 1, 'b': 2}.entries.whereValue((v) => v == 1).toMap(),
{'a': 1},
);
expect(
{'a': 1, 'b': 2}.entries.whereValue((v) => v == 2).toMap(),
{'b': 2},
);
expect(
{'a': 1, 'b': 2}.entries.whereValue((v) => v != 3).toMap(),
{'a': 1, 'b': 2},
);
});
});
group('.keys', () {
test('empty', () {
expect(<String, int>{}.entries.keys, isEmpty);
});
test('single', () {
expect({'a': 1}.entries.keys, ['a']);
});
test('multiple', () {
expect({'a': 1, 'b': 2}.entries.keys, ['a', 'b']);
});
});
group('.values', () {
test('empty', () {
expect(<String, int>{}.entries.values, isEmpty);
});
test('single', () {
expect({'a': 1}.entries.values, [1]);
});
test('multiple', () {
expect({'a': 1, 'b': 2}.entries.values, [1, 2]);
});
});
group('.toMap', () {
test('empty', () {
expect(<String, int>{}.entries.toMap(), <String, int>{});
});
test('single', () {
expect({'a': 1}.entries.toMap(), {'a': 1});
});
test('multiple', () {
expect({'a': 1, 'b': 2}.entries.toMap(), {'a': 1, 'b': 2});
});
});
});
group('of comparable', () {
group('.min', () {
test('empty', () {
Expand Down