Index: pkg/collection/lib/wrappers.dart |
diff --git a/pkg/collection/lib/wrappers.dart b/pkg/collection/lib/wrappers.dart |
index 0fcc8c23e80a42f6e86c84ebea3988f512cfca02..36204f527a402cf12597315d58aed91dc9cc84fe 100644 |
--- a/pkg/collection/lib/wrappers.dart |
+++ b/pkg/collection/lib/wrappers.dart |
@@ -19,19 +19,15 @@ export "dart:collection" show UnmodifiableListView; |
part "src/unmodifiable_wrappers.dart"; |
/** |
- * Creates an [Iterable] that delegates all operations to a base iterable. |
+ * A base class for delegating iterables. |
* |
- * This class can be used hide non-`Iterable` methods of an iterable object, |
- * or it can be extended to add extra functionality on top of an existing |
- * iterable object. |
+ * Subclasses can provide a [_base] that should be delegated to. Unlike |
+ * [DelegatingIterable], this allows the base to be created on demand. |
*/ |
-class DelegatingIterable<E> implements Iterable<E> { |
- final Iterable<E> _base; |
+abstract class _DelegatingIterableBase<E> implements Iterable<E> { |
+ Iterable<E> get _base; |
- /** |
- * Create a wrapper that forwards operations to [base]. |
- */ |
- const DelegatingIterable(Iterable<E> base) : _base = base; |
+ const _DelegatingIterableBase(); |
bool any(bool test(E element)) => _base.any(test); |
@@ -93,6 +89,22 @@ class DelegatingIterable<E> implements Iterable<E> { |
String toString() => _base.toString(); |
} |
+/** |
+ * Creates an [Iterable] that delegates all operations to a base iterable. |
+ * |
+ * This class can be used hide non-`Iterable` methods of an iterable object, |
+ * or it can be extended to add extra functionality on top of an existing |
+ * iterable object. |
+ */ |
+class DelegatingIterable<E> extends _DelegatingIterableBase<E> { |
+ final Iterable<E> _base; |
+ |
+ /** |
+ * Create a wrapper that forwards operations to [base]. |
+ */ |
+ const DelegatingIterable(Iterable<E> base) : _base = base; |
+} |
+ |
/** |
* Creates a [List] that delegates all operations to a base list. |
@@ -337,3 +349,134 @@ class DelegatingMap<K, V> implements Map<K, V> { |
String toString() => _base.toString(); |
} |
+ |
+/** |
+ * Creates an unmodifiable [Set] that delegates all operations to a base map's |
Lasse Reichstein Nielsen
2014/05/09 09:00:17
How about:
An unmodifiable [Set] view of the key
nweiz
2014/05/19 20:58:48
Done.
|
+ * keys. |
+ * |
+ * This class can be used to make a `Map` look like a `Set`. Note that [lookup] |
+ * is not `O(1)` for this set. |
Lasse Reichstein Nielsen
2014/05/09 09:00:17
Or say that it is unsupported, if we can't find a
floitsch
2014/05/09 09:49:41
I think it's ok to have the slow method.
Lasse Reichstein Nielsen
2014/05/09 10:50:48
It's not that it's slow, but that it's wrong.
Test
Lasse Reichstein Nielsen
2014/05/09 10:52:52
Should ofcourse be:
var m = new LinkedHashMap.id
nweiz
2014/05/19 20:58:48
Marked as unsupported.
|
+ */ |
+class MapKeySet<E> extends _DelegatingIterableBase<E> |
+ with UnmodifiableSetMixin<E> { |
+ final Map<E, dynamic> _baseMap; |
+ |
+ Iterable<E> get _base => _baseMap.keys; |
Lasse Reichstein Nielsen
2014/05/09 09:00:17
Move getter below constructor.
nweiz
2014/05/19 20:58:48
Done.
|
+ |
+ MapKeySet(Map<E, dynamic> base) : _baseMap = base; |
+ |
+ bool contains(Object element) => _baseMap.containsKey(element); |
+ |
+ bool get isEmpty => _baseMap.isEmpty; |
+ |
+ bool get isNotEmpty => _baseMap.isNotEmpty; |
+ |
+ int get length => _baseMap.length; |
+ |
+ String toString() => toSet().toString(); |
Lasse Reichstein Nielsen
2014/05/09 09:00:17
The toSet operation may lose elements if the base
floitsch
2014/05/09 09:49:41
Why not simply return "{${_base.join(', ')}}"; ?
Lasse Reichstein Nielsen
2014/05/09 10:50:48
Yes, I think I mistakenly thought that _baseMap.ke
nweiz
2014/05/19 20:58:48
Done.
|
+ |
+ bool containsAll(Iterable<Object> other) => other.every(contains); |
+ |
+ Set<E> difference(Set<E> other) => |
Lasse Reichstein Nielsen
2014/05/09 09:00:17
It should be commented that the set returned here
nweiz
2014/05/19 20:58:48
Done.
|
+ where((element) => !other.contains(element)).toSet(); |
+ |
+ Set<E> intersection(Set<Object> other) => |
+ where((element) => other.contains(element)).toSet(); |
Lasse Reichstein Nielsen
2014/05/09 09:00:17
"(element) => other.contains(element)" can be just
nweiz
2014/05/19 20:58:48
Done.
|
+ |
+ E lookup(E element) => firstWhere((key) => element == key, |
Lasse Reichstein Nielsen
2014/05/09 09:00:17
Using == here might be incompatible with the actua
floitsch
2014/05/09 09:49:41
I think that's too unexpected.
We should probably
nweiz
2014/05/19 20:58:48
I'm happy to do whichever you two agree on. For no
|
+ orElse: () => null); |
+ |
+ Set<E> union(Set<E> other) => toSet()..addAll(other); |
+} |
+ |
+/** |
+ * Creates a [Set] that delegates all operations to a base map's values. |
+ * |
+ * This class can be used to make a `Map` whose keys are determinable from its |
+ * values look like a `Set`. |
Lasse Reichstein Nielsen
2014/05/09 09:00:17
How about:
----
Creates a modifiable [Set] view o
nweiz
2014/05/19 20:58:48
Done.
|
+ * |
+ * For example, this might be used to expose a map from IDs to database records |
+ * as a Set. The `keyForValue` function would be `(record) => record.id`, and |
+ * this would allow users to write both `recordSet.add(databaseRecord)` and |
+ * `recordMap[id]`. |
+ */ |
+class MapValueSet<K, V> extends _DelegatingIterableBase<V> implements Set<V> { |
+ final Map<K, V> _baseMap; |
+ final Function _keyForValue; |
+ |
+ Iterable<V> get _base => _baseMap.values; |
floitsch
2014/05/09 09:49:41
getter below constructor.
nweiz
2014/05/19 20:58:48
Done.
|
+ |
+ /** |
+ * Creates a new [MapValueSet] based on [base]. |
+ * |
+ * [keyForValue] returns the key in the map that should be associated with the |
+ * given value. The set's notion of equality is identical to the equality of |
+ * the return values of [keyForValue]. |
+ */ |
+ MapValueSet(Map<K, V> base, K keyForValue(V value)) |
+ : _baseMap = base, |
+ _keyForValue = keyForValue; |
+ |
+ bool contains(Object element) { |
+ if (element is! V) return false; |
+ return _baseMap.containsKey(_keyForValue(element)); |
+ } |
+ |
+ bool get isEmpty => _baseMap.isEmpty; |
+ |
+ bool get isNotEmpty => _baseMap.isNotEmpty; |
+ |
+ int get length => _baseMap.length; |
+ |
+ String toString() => toSet().toString(); |
+ |
+ bool add(V value) { |
+ var key = _keyForValue(value); |
floitsch
2014/05/09 09:49:41
K key = _keyForValue...
nweiz
2014/05/19 20:58:48
Done, although this is contrary to the style guide
|
+ if (_baseMap.containsKey(key)) return false; |
+ _baseMap[key] = value; |
Lasse Reichstein Nielsen
2014/05/09 09:00:17
Maybe use:
bool result = false;
_baseMap.putIf
nweiz
2014/05/19 20:58:48
Done.
|
+ return true; |
+ } |
+ |
+ void addAll(Iterable<V> elements) => elements.forEach(add); |
+ |
+ void clear() => _baseMap.clear(); |
+ |
+ bool containsAll(Iterable<Object> other) => other.every(contains); |
+ |
+ Set<V> difference(Set<V> other) => |
Lasse Reichstein Nielsen
2014/05/09 09:00:17
Document that the returned set is not of the same
nweiz
2014/05/19 20:58:48
Done.
|
+ where((element) => !other.contains(element)).toSet(); |
+ |
+ Set<V> intersection(Set<Object> other) => |
+ where((element) => other.contains(element)).toSet(); |
Lasse Reichstein Nielsen
2014/05/09 09:00:17
where(other.contains).toSet()
nweiz
2014/05/19 20:58:48
Done.
|
+ |
+ V lookup(V element) => _baseMap[_keyForValue(element)]; |
+ |
+ bool remove(Object value) { |
+ if (value is! V) return false; |
+ var key = _keyForValue(value); |
+ if (!_baseMap.containsKey(key)) return false; |
Lasse Reichstein Nielsen
2014/05/09 09:00:17
return _baseMap.remove(key) != null;
Since null v
nweiz
2014/05/19 20:58:48
I don't think we should necessarily disallow null.
|
+ _baseMap.remove(key); |
+ return true; |
+ } |
+ |
+ void removeAll(Iterable<Object> elements) => elements.forEach(remove); |
+ |
+ void removeWhere(bool test(V element)) { |
+ new Map.from(_baseMap).forEach((key, value) { |
+ if (test(value)) remove(key); |
Lasse Reichstein Nielsen
2014/05/09 09:00:17
-> _baseMap.remove(key)
The new map may not agree
nweiz
2014/05/19 20:58:48
Done.
|
+ }); |
+ } |
+ |
+ void retainAll(Iterable<Object> elements) { |
Lasse Reichstein Nielsen
2014/05/09 09:00:17
I *so* regret keeping retainAll in the Set interfa
|
+ var retainKeys = elements.where((element) => element is V) |
+ .map((element) => _keyForValue(element)).toSet(); |
Lasse Reichstein Nielsen
2014/05/09 09:00:17
There is a problem with equality here too. The equ
nweiz
2014/05/19 20:58:48
According to the retainAll documentation, element
|
+ new Map.from(_baseMap).forEach((key, value) { |
+ if (!retainKeys.contains(key)) remove(key); |
+ }); |
+ } |
+ |
+ void retainWhere(bool test(V element)) => |
+ removeWhere((element) => !test(element)); |
+ |
+ Set<V> union(Set<V> other) => toSet()..addAll(other); |
+} |