Skip to content

Commit 9e37915

Browse files
authored
Introduce IterableMapEntryExtension for use with Map.entries. (#715)
* Introduce `IterableMapEntryExtension` for use with `Map.entries`. **Example**: ```dart final myMap = { 'foo': 42, 'bar': -1, 'foobar': 21, }; // myMap without negative values myMap.entries.whereValue((v) => v >= 0).toMap(); // myMap, but only keys that start with 'foo' myMap.entries.whereKey((k) => k.startsWith('foo')).toMap(); ```
1 parent 4b62792 commit 9e37915

File tree

4 files changed

+216
-1
lines changed

4 files changed

+216
-1
lines changed

pkgs/collection/CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 1.19.1-wip
2+
- Add `IterableMapEntryExtension` for working on `Map` as a list of pairs, using
3+
`Map.entries`.
4+
15
## 1.19.1
26

37
- Move to `dart-lang/core` monorepo.

pkgs/collection/lib/src/iterable_extensions.dart

+37
Original file line numberDiff line numberDiff line change
@@ -914,6 +914,43 @@ extension IterableIterableExtension<T> on Iterable<Iterable<T>> {
914914
};
915915
}
916916

917+
/// Extension on iterables of [MapEntry].
918+
///
919+
/// An [Iterable<MapEntry>] is obtained using [Map.entries]. These extensions
920+
/// facilitates working directly on the entries of a [Map].
921+
extension IterableMapEntryExtension<K, V> on Iterable<MapEntry<K, V>> {
922+
/// The elements whose [MapEntry.key] values satisfy [test].
923+
///
924+
/// The resulting iterable is lazily computing its elements
925+
/// based on the elements this iterable.
926+
Iterable<MapEntry<K, V>> whereKey(bool Function(K) test) =>
927+
where((e) => test(e.key));
928+
929+
/// The elements whose [MapEntry.value] values satisfy [test].
930+
///
931+
/// The resulting iterable is lazily computing its elements
932+
/// based on the elements this iterable.
933+
Iterable<MapEntry<K, V>> whereValue(bool Function(V) test) =>
934+
where((e) => test(e.value));
935+
936+
/// A new lazy [Iterable] of the [MapEntry.key]s of these entries.
937+
///
938+
/// Do not use this getter as `map.entries.keys`, just use `map.keys`
939+
/// directly.
940+
Iterable<K> get keys => map((e) => e.key);
941+
942+
/// A new lazy [Iterable] of the [MapEntry.value]s of these entries.
943+
///
944+
/// Do not use this getter as `map.entries.values`, just use `map.values`
945+
/// directly.
946+
Iterable<V> get values => map((e) => e.value);
947+
948+
/// Create a [Map<K, V>] from all elements.
949+
///
950+
/// This is a short-hand for [Map.fromEntries].
951+
Map<K, V> toMap() => Map<K, V>.fromEntries(this);
952+
}
953+
917954
/// Extensions that apply to iterables of [Comparable] elements.
918955
///
919956
/// These operations can assume that the elements have a natural ordering,

pkgs/collection/pubspec.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: collection
2-
version: 1.19.1
2+
version: 1.19.1-wip
33
description: >-
44
Collections and utilities functions and classes related to collections.
55
repository: https://github.com/dart-lang/core/tree/main/pkgs/collection

pkgs/collection/test/extensions_test.dart

+174
Original file line numberDiff line numberDiff line change
@@ -1122,6 +1122,180 @@ void main() {
11221122
});
11231123
});
11241124
});
1125+
group('of MapEntry', () {
1126+
group('.whereKey', () {
1127+
test('empty', () {
1128+
expect(
1129+
iterable(<MapEntry<String, int>>[]).whereKey(unreachable),
1130+
isEmpty,
1131+
);
1132+
});
1133+
test('single', () {
1134+
expect(
1135+
iterable([const MapEntry('a', 1)]).whereKey((k) => k == 'a'),
1136+
[const MapEntry('a', 1)],
1137+
);
1138+
expect(
1139+
iterable([const MapEntry('a', 1)]).whereKey((k) => k == 'b'),
1140+
isEmpty,
1141+
);
1142+
});
1143+
test('multiple', () {
1144+
expect(
1145+
iterable([
1146+
const MapEntry('a', 1),
1147+
const MapEntry('b', 2),
1148+
]).whereKey((k) => k == 'a'),
1149+
[const MapEntry('a', 1)],
1150+
);
1151+
expect(
1152+
iterable([
1153+
const MapEntry('a', 1),
1154+
const MapEntry('b', 2),
1155+
]).whereKey((k) => k == 'b'),
1156+
[const MapEntry('b', 2)],
1157+
);
1158+
expect(
1159+
iterable([
1160+
const MapEntry('a', 1),
1161+
const MapEntry('b', 2),
1162+
]).whereKey((k) => k != 'c'),
1163+
[const MapEntry('a', 1), const MapEntry('b', 2)],
1164+
);
1165+
expect(
1166+
iterable([
1167+
const MapEntry('a', 1),
1168+
const MapEntry('b', 2),
1169+
const MapEntry('a', 3),
1170+
]).whereKey((k) => k == 'a'),
1171+
[const MapEntry('a', 1), const MapEntry('a', 3)],
1172+
);
1173+
});
1174+
});
1175+
group('.whereValue', () {
1176+
test('empty', () {
1177+
expect(
1178+
iterable(<MapEntry<String, int>>[]).whereValue(unreachable),
1179+
isEmpty,
1180+
);
1181+
});
1182+
test('single', () {
1183+
expect(
1184+
iterable([const MapEntry('a', 1)]).whereValue((v) => v == 1),
1185+
[const MapEntry('a', 1)],
1186+
);
1187+
expect(
1188+
iterable([const MapEntry('a', 1)]).whereValue((v) => v == 2),
1189+
isEmpty,
1190+
);
1191+
});
1192+
test('multiple', () {
1193+
expect(
1194+
iterable([
1195+
const MapEntry('a', 1),
1196+
const MapEntry('b', 2),
1197+
]).whereValue((v) => v == 1),
1198+
[const MapEntry('a', 1)],
1199+
);
1200+
expect(
1201+
iterable([
1202+
const MapEntry('a', 1),
1203+
const MapEntry('b', 2),
1204+
]).whereValue((v) => v == 2),
1205+
[const MapEntry('b', 2)],
1206+
);
1207+
expect(
1208+
iterable([
1209+
const MapEntry('a', 1),
1210+
const MapEntry('b', 2),
1211+
]).whereValue((v) => v != 3),
1212+
[const MapEntry('a', 1), const MapEntry('b', 2)],
1213+
);
1214+
expect(
1215+
iterable([
1216+
const MapEntry('a', 1),
1217+
const MapEntry('b', 2),
1218+
const MapEntry('c', 1),
1219+
]).whereValue((v) => v == 1),
1220+
[const MapEntry('a', 1), const MapEntry('c', 1)],
1221+
);
1222+
expect(
1223+
iterable([
1224+
const MapEntry('a', 1),
1225+
const MapEntry('b', 2),
1226+
const MapEntry('a', 1),
1227+
]).whereValue((v) => v == 1),
1228+
[const MapEntry('a', 1), const MapEntry('a', 1)],
1229+
);
1230+
});
1231+
});
1232+
group('.keys', () {
1233+
test('empty', () {
1234+
expect(iterable(<MapEntry<String, int>>[]).keys, isEmpty);
1235+
});
1236+
test('single', () {
1237+
expect(iterable([const MapEntry('a', 1)]).keys, ['a']);
1238+
});
1239+
test('multiple', () {
1240+
expect(
1241+
iterable([const MapEntry('a', 1), const MapEntry('b', 2)]).keys,
1242+
['a', 'b'],
1243+
);
1244+
expect(
1245+
iterable([
1246+
const MapEntry('a', 1),
1247+
const MapEntry('b', 2),
1248+
const MapEntry('a', 3),
1249+
]).keys,
1250+
['a', 'b', 'a'],
1251+
);
1252+
});
1253+
});
1254+
group('.values', () {
1255+
test('empty', () {
1256+
expect(iterable(<MapEntry<String, int>>[]).values, isEmpty);
1257+
});
1258+
test('single', () {
1259+
expect(iterable([const MapEntry('a', 1)]).values, [1]);
1260+
});
1261+
test('multiple', () {
1262+
expect(
1263+
iterable([const MapEntry('a', 1), const MapEntry('b', 2)]).values,
1264+
[1, 2],
1265+
);
1266+
expect(
1267+
iterable([
1268+
const MapEntry('a', 1),
1269+
const MapEntry('b', 2),
1270+
const MapEntry('a', 3),
1271+
]).values,
1272+
[1, 2, 3],
1273+
);
1274+
});
1275+
});
1276+
group('.toMap', () {
1277+
test('empty', () {
1278+
expect(iterable(<MapEntry<String, int>>[]).toMap(), <String, int>{});
1279+
});
1280+
test('single', () {
1281+
expect(iterable([const MapEntry('a', 1)]).toMap(), {'a': 1});
1282+
});
1283+
test('multiple', () {
1284+
expect(
1285+
iterable([const MapEntry('a', 1), const MapEntry('b', 2)]).toMap(),
1286+
{'a': 1, 'b': 2},
1287+
);
1288+
expect(
1289+
iterable([
1290+
const MapEntry('a', 1),
1291+
const MapEntry('b', 2),
1292+
const MapEntry('a', 3),
1293+
]).toMap(),
1294+
{'b': 2, 'a': 3},
1295+
);
1296+
});
1297+
});
1298+
});
11251299
group('of comparable', () {
11261300
group('.min', () {
11271301
test('empty', () {

0 commit comments

Comments
 (0)