Path: blob/trunk/third_party/closure/goog/labs/structs/multimap.js
2868 views
// Copyright 2012 The Closure Library Authors. All Rights Reserved.1//2// Licensed under the Apache License, Version 2.0 (the "License");3// you may not use this file except in compliance with the License.4// You may obtain a copy of the License at5//6// http://www.apache.org/licenses/LICENSE-2.07//8// Unless required by applicable law or agreed to in writing, software9// distributed under the License is distributed on an "AS-IS" BASIS,10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.11// See the License for the specific language governing permissions and12// limitations under the License.1314/**15* @fileoverview A collection similar to16* {@code goog.labs.structs.Map}, but also allows associating multiple17* values with a single key.18*19* This implementation ensures that you can use any string keys.20*21* @author [email protected] (Chris Henry)22*/2324goog.provide('goog.labs.structs.Multimap');2526goog.require('goog.array');27goog.require('goog.labs.structs.Map');28goog.require('goog.object');29303132/**33* Creates a new multimap.34* @constructor35* @struct36* @final37*/38goog.labs.structs.Multimap = function() {39this.clear();40};414243/**44* The backing map.45* @type {!goog.labs.structs.Map}46* @private47*/48goog.labs.structs.Multimap.prototype.map_;495051/**52* @type {number}53* @private54*/55goog.labs.structs.Multimap.prototype.count_ = 0;565758/**59* Clears the multimap.60*/61goog.labs.structs.Multimap.prototype.clear = function() {62this.count_ = 0;63this.map_ = new goog.labs.structs.Map();64};656667/**68* Clones this multimap.69* @return {!goog.labs.structs.Multimap} A multimap that contains all70* the mapping this multimap has.71*/72goog.labs.structs.Multimap.prototype.clone = function() {73var map = new goog.labs.structs.Multimap();74map.addAllFromMultimap(this);75return map;76};777879/**80* Adds the given (key, value) pair to the map. The (key, value) pair81* is guaranteed to be added.82* @param {string} key The key to add.83* @param {*} value The value to add.84*/85goog.labs.structs.Multimap.prototype.add = function(key, value) {86var values = this.map_.get(key);87if (!values) {88this.map_.set(key, (values = []));89}9091values.push(value);92this.count_++;93};949596/**97* Stores a collection of values to the given key. Does not replace98* existing (key, value) pairs.99* @param {string} key The key to add.100* @param {!Array<*>} values The values to add.101*/102goog.labs.structs.Multimap.prototype.addAllValues = function(key, values) {103goog.array.forEach(values, function(v) { this.add(key, v); }, this);104};105106107/**108* Adds the contents of the given map/multimap to this multimap.109* @param {!(goog.labs.structs.Map|goog.labs.structs.Multimap)} map The110* map to add.111*/112goog.labs.structs.Multimap.prototype.addAllFromMultimap = function(map) {113goog.array.forEach(map.getEntries(), function(entry) {114this.add(entry[0], entry[1]);115}, this);116};117118119/**120* Replaces all the values for the given key with the given values.121* @param {string} key The key whose values are to be replaced.122* @param {!Array<*>} values The new values. If empty, this is123* equivalent to {@code removaAll(key)}.124*/125goog.labs.structs.Multimap.prototype.replaceValues = function(key, values) {126this.removeAll(key);127this.addAllValues(key, values);128};129130131/**132* Gets the values correspond to the given key.133* @param {string} key The key to retrieve.134* @return {!Array<*>} An array of values corresponding to the given135* key. May be empty. Note that the ordering of values are not136* guaranteed to be consistent.137*/138goog.labs.structs.Multimap.prototype.get = function(key) {139var values = /** @type {Array<*>} */ (this.map_.get(key));140return values ? goog.array.clone(values) : [];141};142143144/**145* Removes a single occurrence of (key, value) pair.146* @param {string} key The key to remove.147* @param {*} value The value to remove.148* @return {boolean} Whether any matching (key, value) pair is removed.149*/150goog.labs.structs.Multimap.prototype.remove = function(key, value) {151var values = /** @type {Array<*>} */ (this.map_.get(key));152if (!values) {153return false;154}155156var removed = goog.array.removeIf(157values, function(v) { return goog.object.is(value, v); });158159if (removed) {160this.count_--;161if (values.length == 0) {162this.map_.remove(key);163}164}165return removed;166};167168169/**170* Removes all values corresponding to the given key.171* @param {string} key The key whose values are to be removed.172* @return {boolean} Whether any value is removed.173*/174goog.labs.structs.Multimap.prototype.removeAll = function(key) {175// We have to first retrieve the values from the backing map because176// we need to keep track of count (and correctly calculates the177// return value). values may be undefined.178var values = this.map_.get(key);179if (this.map_.remove(key)) {180this.count_ -= values.length;181return true;182}183184return false;185};186187188/**189* @return {boolean} Whether the multimap is empty.190*/191goog.labs.structs.Multimap.prototype.isEmpty = function() {192return !this.count_;193};194195196/**197* @return {number} The count of (key, value) pairs in the map.198*/199goog.labs.structs.Multimap.prototype.getCount = function() {200return this.count_;201};202203204/**205* @param {string} key The key to check.206* @param {*} value The value to check.207* @return {boolean} Whether the (key, value) pair exists in the multimap.208*/209goog.labs.structs.Multimap.prototype.containsEntry = function(key, value) {210var values = /** @type {Array<*>} */ (this.map_.get(key));211if (!values) {212return false;213}214215var index = goog.array.findIndex(216values, function(v) { return goog.object.is(v, value); });217return index >= 0;218};219220221/**222* @param {string} key The key to check.223* @return {boolean} Whether the multimap contains at least one (key,224* value) pair with the given key.225*/226goog.labs.structs.Multimap.prototype.containsKey = function(key) {227return this.map_.containsKey(key);228};229230231/**232* @param {*} value The value to check.233* @return {boolean} Whether the multimap contains at least one (key,234* value) pair with the given value.235*/236goog.labs.structs.Multimap.prototype.containsValue = function(value) {237return goog.array.some(this.map_.getValues(), function(values) {238return goog.array.some(/** @type {Array<?>} */ (values), function(v) {239return goog.object.is(v, value);240});241});242};243244245/**246* @return {!Array<string>} An array of unique keys.247*/248goog.labs.structs.Multimap.prototype.getKeys = function() {249return this.map_.getKeys();250};251252253/**254* @return {!Array<*>} An array of values. There may be duplicates.255*/256goog.labs.structs.Multimap.prototype.getValues = function() {257return goog.array.flatten(this.map_.getValues());258};259260261/**262* @return {!Array<!Array<?>>} An array of entries. Each entry is of the263* form [key, value].264*/265goog.labs.structs.Multimap.prototype.getEntries = function() {266var keys = this.getKeys();267var entries = [];268for (var i = 0; i < keys.length; i++) {269var key = keys[i];270var values = this.get(key);271for (var j = 0; j < values.length; j++) {272entries.push([key, values[j]]);273}274}275return entries;276};277278279