-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add CombinedMapView (dart-archive/collection#53)
* Add CombinedMapView. * Mistype. * Address feedback. * Address feedback.
- Loading branch information
1 parent
8d1a887
commit d81d3d8
Showing
4 changed files
with
112 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
52 changes: 52 additions & 0 deletions
52
pkgs/collection/lib/src/combined_wrappers/combined_map.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file | ||
// for details. All rights reserved. Use of this source code is governed by a | ||
// BSD-style license that can be found in the LICENSE file. | ||
|
||
import 'dart:collection'; | ||
|
||
import 'combined_iterable.dart'; | ||
|
||
/// Returns a new map that represents maps flattened into a single map. | ||
/// | ||
/// All methods and accessors treat the new map as-if it were a single | ||
/// concatenated map, but the underlying implementation is based on lazily | ||
/// accessing individual map instances. In the occasion where a key occurs in | ||
/// multiple maps the first value is returned. | ||
/// | ||
/// The resulting map has an index operator (`[]`) and `length` property that | ||
/// are both `O(maps)`, rather than `O(1)`, and the map is unmodifiable - but | ||
/// underlying changes to these maps are still accessible from the resulting | ||
/// map. | ||
class CombinedMapView<K, V> extends UnmodifiableMapBase<K, V> { | ||
final Iterable<Map<K, V>> _maps; | ||
|
||
/// Create a new combined view into multiple maps. | ||
/// | ||
/// The iterable is accessed lazily so it should be collection type like | ||
/// [List] or [Set] rather than a lazy iterable produced by `map()` et al. | ||
CombinedMapView(this._maps); | ||
|
||
V operator [](Object key) { | ||
for (var map in _maps) { | ||
// Avoid two hash lookups on a positive hit. | ||
var value = map[key]; | ||
if (value != null || map.containsKey(value)) { | ||
return value; | ||
} | ||
} | ||
return null; | ||
} | ||
|
||
/// The keys of [this]. | ||
/// | ||
/// The returned iterable has efficient `length` and `contains` operations, | ||
/// based on [length] and [containsKey] of the individual maps. | ||
/// | ||
/// The order of iteration is defined by the individual `Map` implementations, | ||
/// but must be consistent between changes to the maps. | ||
/// | ||
/// Unlike most [Map] implementations, modifying an individual map while | ||
/// iterating the keys will _sometimes_ throw. This behavior may change in | ||
/// the future. | ||
Iterable<K> get keys => new CombinedIterableView<K>(_maps.map((m) => m.keys)); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file | ||
// for details. All rights reserved. Use of this source code is governed by a | ||
// BSD-style license that can be found in the LICENSE file. | ||
|
||
import 'package:collection/collection.dart'; | ||
import 'package:test/test.dart'; | ||
|
||
import '../unmodifiable_collection_test.dart' as common; | ||
|
||
void main() { | ||
var map1 = const {1: 1, 2: 2, 3: 3}; | ||
var map2 = const {4: 4, 5: 5, 6: 6}; | ||
var map3 = const {7: 7, 8: 8, 9: 9}; | ||
var concat = {}..addAll(map1)..addAll(map2)..addAll(map3); | ||
|
||
// In every way possible this should test the same as an UnmodifiableMapView. | ||
common.testReadMap(concat, new CombinedMapView( | ||
[map1, map2, map3] | ||
), 'CombinedMapView'); | ||
|
||
common.testReadMap(concat, new CombinedMapView( | ||
[map1, {}, map2, {}, map3, {}] | ||
), 'CombinedMapView (some empty)'); | ||
|
||
test('should function as an empty map when no maps are passed', () { | ||
var empty = new CombinedMapView([]); | ||
expect(empty, isEmpty); | ||
expect(empty.length, 0); | ||
}); | ||
|
||
test('should function as an empty map when only empty maps are passed', () { | ||
var empty = new CombinedMapView([{}, {}, {}]); | ||
expect(empty, isEmpty); | ||
expect(empty.length, 0); | ||
}); | ||
|
||
test('should reflect underlying changes back to the combined map', () { | ||
var backing1 = <int, int>{}; | ||
var backing2 = <int, int>{}; | ||
var combined = new CombinedMapView([backing1, backing2]); | ||
expect(combined, isEmpty); | ||
backing1.addAll(map1); | ||
expect(combined, map1); | ||
backing2.addAll(map2); | ||
expect(combined, new Map.from(backing1)..addAll(backing2)); | ||
}); | ||
|
||
test('should reflect underlying changes with a single map', () { | ||
var backing1 = <int, int>{}; | ||
var combined = new CombinedMapView([backing1]); | ||
expect(combined, isEmpty); | ||
backing1.addAll(map1); | ||
expect(combined, map1); | ||
}); | ||
} |