Chromium Code Reviews| 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); |
| +} |