diff --git a/pkgs/collection/CHANGELOG.md b/pkgs/collection/CHANGELOG.md index 021dd937..239efd57 100644 --- a/pkgs/collection/CHANGELOG.md +++ b/pkgs/collection/CHANGELOG.md @@ -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. diff --git a/pkgs/collection/lib/src/iterable_extensions.dart b/pkgs/collection/lib/src/iterable_extensions.dart index e2050062..10f46760 100644 --- a/pkgs/collection/lib/src/iterable_extensions.dart +++ b/pkgs/collection/lib/src/iterable_extensions.dart @@ -914,6 +914,43 @@ extension IterableIterableExtension on Iterable> { }; } +/// Extension on iterables of [MapEntry]. +/// +/// An [Iterable] is obtained using [Map.entries]. These extensions +/// facilitates working directly on the entries of a [Map]. +extension IterableMapEntryExtension on Iterable> { + /// The elements whose [MapEntry.key] values satisfy [test]. + /// + /// The resulting iterable is lazily computing its elements + /// based on the elements this iterable. + Iterable> whereKey(bool Function(K) test) => + where((e) => test(e.key)); + + /// The elements whose [MapEntry.value] values satisfy [test]. + /// + /// The resulting iterable is lazily computing its elements + /// based on the elements this iterable. + Iterable> whereValue(bool Function(V) test) => + where((e) => test(e.value)); + + /// A new lazy [Iterable] of the [MapEntry.key]s of these entries. + /// + /// Do not use this getter as `map.entries.keys`, just use `map.keys` + /// directly. + Iterable get keys => map((e) => e.key); + + /// A new lazy [Iterable] of the [MapEntry.value]s of these entries. + /// + /// Do not use this getter as `map.entries.values`, just use `map.values` + /// directly. + Iterable get values => map((e) => e.value); + + /// Create a [Map] from all elements. + /// + /// This is a short-hand for [Map.fromEntries]. + Map toMap() => Map.fromEntries(this); +} + /// Extensions that apply to iterables of [Comparable] elements. /// /// These operations can assume that the elements have a natural ordering, diff --git a/pkgs/collection/pubspec.yaml b/pkgs/collection/pubspec.yaml index c1b16334..4e74ac7a 100644 --- a/pkgs/collection/pubspec.yaml +++ b/pkgs/collection/pubspec.yaml @@ -1,5 +1,5 @@ name: collection -version: 1.19.1 +version: 1.19.1-wip description: >- Collections and utilities functions and classes related to collections. repository: https://github.com/dart-lang/core/tree/main/pkgs/collection diff --git a/pkgs/collection/test/extensions_test.dart b/pkgs/collection/test/extensions_test.dart index 9940e1d4..6c5b45f1 100644 --- a/pkgs/collection/test/extensions_test.dart +++ b/pkgs/collection/test/extensions_test.dart @@ -1122,6 +1122,180 @@ void main() { }); }); }); + group('of MapEntry', () { + group('.whereKey', () { + test('empty', () { + expect( + iterable(>[]).whereKey(unreachable), + isEmpty, + ); + }); + test('single', () { + expect( + iterable([const MapEntry('a', 1)]).whereKey((k) => k == 'a'), + [const MapEntry('a', 1)], + ); + expect( + iterable([const MapEntry('a', 1)]).whereKey((k) => k == 'b'), + isEmpty, + ); + }); + test('multiple', () { + expect( + iterable([ + const MapEntry('a', 1), + const MapEntry('b', 2), + ]).whereKey((k) => k == 'a'), + [const MapEntry('a', 1)], + ); + expect( + iterable([ + const MapEntry('a', 1), + const MapEntry('b', 2), + ]).whereKey((k) => k == 'b'), + [const MapEntry('b', 2)], + ); + expect( + iterable([ + const MapEntry('a', 1), + const MapEntry('b', 2), + ]).whereKey((k) => k != 'c'), + [const MapEntry('a', 1), const MapEntry('b', 2)], + ); + expect( + iterable([ + const MapEntry('a', 1), + const MapEntry('b', 2), + const MapEntry('a', 3), + ]).whereKey((k) => k == 'a'), + [const MapEntry('a', 1), const MapEntry('a', 3)], + ); + }); + }); + group('.whereValue', () { + test('empty', () { + expect( + iterable(>[]).whereValue(unreachable), + isEmpty, + ); + }); + test('single', () { + expect( + iterable([const MapEntry('a', 1)]).whereValue((v) => v == 1), + [const MapEntry('a', 1)], + ); + expect( + iterable([const MapEntry('a', 1)]).whereValue((v) => v == 2), + isEmpty, + ); + }); + test('multiple', () { + expect( + iterable([ + const MapEntry('a', 1), + const MapEntry('b', 2), + ]).whereValue((v) => v == 1), + [const MapEntry('a', 1)], + ); + expect( + iterable([ + const MapEntry('a', 1), + const MapEntry('b', 2), + ]).whereValue((v) => v == 2), + [const MapEntry('b', 2)], + ); + expect( + iterable([ + const MapEntry('a', 1), + const MapEntry('b', 2), + ]).whereValue((v) => v != 3), + [const MapEntry('a', 1), const MapEntry('b', 2)], + ); + expect( + iterable([ + const MapEntry('a', 1), + const MapEntry('b', 2), + const MapEntry('c', 1), + ]).whereValue((v) => v == 1), + [const MapEntry('a', 1), const MapEntry('c', 1)], + ); + expect( + iterable([ + const MapEntry('a', 1), + const MapEntry('b', 2), + const MapEntry('a', 1), + ]).whereValue((v) => v == 1), + [const MapEntry('a', 1), const MapEntry('a', 1)], + ); + }); + }); + group('.keys', () { + test('empty', () { + expect(iterable(>[]).keys, isEmpty); + }); + test('single', () { + expect(iterable([const MapEntry('a', 1)]).keys, ['a']); + }); + test('multiple', () { + expect( + iterable([const MapEntry('a', 1), const MapEntry('b', 2)]).keys, + ['a', 'b'], + ); + expect( + iterable([ + const MapEntry('a', 1), + const MapEntry('b', 2), + const MapEntry('a', 3), + ]).keys, + ['a', 'b', 'a'], + ); + }); + }); + group('.values', () { + test('empty', () { + expect(iterable(>[]).values, isEmpty); + }); + test('single', () { + expect(iterable([const MapEntry('a', 1)]).values, [1]); + }); + test('multiple', () { + expect( + iterable([const MapEntry('a', 1), const MapEntry('b', 2)]).values, + [1, 2], + ); + expect( + iterable([ + const MapEntry('a', 1), + const MapEntry('b', 2), + const MapEntry('a', 3), + ]).values, + [1, 2, 3], + ); + }); + }); + group('.toMap', () { + test('empty', () { + expect(iterable(>[]).toMap(), {}); + }); + test('single', () { + expect(iterable([const MapEntry('a', 1)]).toMap(), {'a': 1}); + }); + test('multiple', () { + expect( + iterable([const MapEntry('a', 1), const MapEntry('b', 2)]).toMap(), + {'a': 1, 'b': 2}, + ); + expect( + iterable([ + const MapEntry('a', 1), + const MapEntry('b', 2), + const MapEntry('a', 3), + ]).toMap(), + {'b': 2, 'a': 3}, + ); + }); + }); + }); group('of comparable', () { group('.min', () { test('empty', () {