@itwin/core-bentley
Version:
Bentley JavaScript core components
209 lines • 8.94 kB
JavaScript
"use strict";
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
/** @packageDocumentation
* @module Collections
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.Dictionary = void 0;
const SortedArray_1 = require("./SortedArray");
class DictionaryIterator {
_keys;
_values;
_curIndex = -1;
constructor(keys, values) {
this._keys = keys;
this._values = values;
}
next() {
if (++this._curIndex >= this._keys.length) {
// The ECMAScript spec states that value=undefined is valid if done=true. The TypeScript interface violates the spec hence the cast to any and back below.
return { done: true };
}
else {
return {
value: {
key: this._keys[this._curIndex],
value: this._values[this._curIndex],
},
done: false,
};
}
}
}
/**
* Maintains a mapping of keys to values.
* Unlike the standard Map<K, V>, a Dictionary<K, V> supports custom comparison logic for keys of object type (and for any other type).
* The user supplies a key comparison function to the constructor, that must meet the following criteria given 'lhs' and 'rhs' of type K:
* - If lhs is equal to rhs, returns 0
* - If lhs is less than rhs, returns a negative value
* - If lhs is greater than rhs, returns a positive value
* - If compare(lhs, rhs) returns 0, then compare(rhs, lhs) must also return 0
* - If compare(lhs, rhs) returns a negative value, then compare(rhs, lhs) must return a positive value, and vice versa.
*
* Modifying a key in a way that affects the comparison function will produce unpredictable results, the
* most likely of which is that keys will cease to map to the values with which they were initially inserted.
* @public
*/
class Dictionary {
_keys = [];
_compareKeys;
_cloneKey;
_values = [];
_cloneValue;
/**
* Construct a new Dictionary<K, V>.
* @param compareKeys The function used to compare keys within the dictionary.
* @param cloneKey The function invoked to clone a key for insertion into the dictionary. The default implementation simply returns its input.
* @param cloneValue The function invoked to clone a value for insertion into the dictionary. The default implementation simply returns its input.
*/
constructor(compareKeys, cloneKey = SortedArray_1.shallowClone, cloneValue = SortedArray_1.shallowClone) {
this._compareKeys = compareKeys;
this._cloneKey = cloneKey;
this._cloneValue = cloneValue;
}
/** The number of entries in the dictionary. */
get size() { return this._keys.length; }
/** Returns an iterator over the key-value pairs in the Dictionary suitable for use in `for-of` loops. Entries are returned in sorted order by key. */
[Symbol.iterator]() {
return new DictionaryIterator(this._keys, this._values);
}
/** Provides iteration over the keys in this Dictionary, in sorted order. */
keys() {
function* iterator(dict) {
for (const entry of dict)
yield entry.key;
}
return {
[Symbol.iterator]: () => iterator(this),
};
}
/** Provides iteration over the values in this Dictionary, in sorted order by the corresponding keys. */
values() {
function* iterator(dict) {
for (const entry of dict)
yield entry.value;
}
return {
[Symbol.iterator]: () => iterator(this),
};
}
/** Removes all entries from this dictionary */
clear() {
this._keys = [];
this._values = [];
}
/**
* Looks up a value by its key.
* @param key The key to search for
* @returns the value associated with the key, or undefined if the key is not present in the dictionary.
*/
get(key) {
const bound = this.lowerBound(key);
return bound.equal ? this._values[bound.index] : undefined;
}
/**
* Determines if an entry exists for the specified key
* @param key The key to search for
* @returns true if an entry exists in this dictionary corresponding to the specified key.
*/
has(key) {
return this.lowerBound(key).equal;
}
/**
* Deletes a value using its key.
* @param key The key to delete
* @returns true if the key was found and deleted.
*/
delete(key) {
const bound = this.lowerBound(key);
if (bound.equal) {
this._values.splice(bound.index, 1);
this._keys.splice(bound.index, 1);
return true;
}
else {
return false;
}
}
/**
* Attempts to insert a new entry into the dictionary. If an entry with an equivalent key exists, the dictionary is unmodified.
* If the new entry is in fact inserted, both the key and value will be cloned using the functions supplied to the dictionary's constructor.
* @param key The key to associate with the value
* @param value The value to associate with the key
* @returns true if the new entry was inserted, false if an entry with an equivalent key already exists.
*/
insert(key, value) {
const result = this.findOrInsert(key, value);
return result.inserted;
}
/** Obtains the value associated with the specified key, or inserts it if the specified key does not yet exist.
* @param key The key to search for.
* @param value The value to associate with `key` if `key` does not yet exist in the dictionary.
* @returns The found or inserted value and a flag indicating whether the new value was inserted.
*/
findOrInsert(key, value) {
const bound = this.lowerBound(key);
if (bound.equal)
return { value: this._values[bound.index], inserted: false };
value = this._cloneValue(value);
this._keys.splice(bound.index, 0, this._cloneKey(key));
this._values.splice(bound.index, 0, this._cloneValue(value));
return { value, inserted: true };
}
/**
* Sets the value associated with the specified key in the dictionary.
* If no such key already exists, this is equivalent to insert(key, value); otherwise, the existing value associated with the key is replaced.
* In either case, the value will be cloned using the function supplied to the dictionary's constructor.
*/
set(key, value) {
value = this._cloneValue(value);
const bound = this.lowerBound(key);
if (bound.equal) {
this._values[bound.index] = value;
}
else {
this._keys.splice(bound.index, 0, this._cloneKey(key));
this._values.splice(bound.index, 0, value);
}
}
/**
* Extracts the contents of this dictionary as an array of { key, value } pairs, and empties this dictionary.
* @returns An array of { key, value } pairs sorted by key.
*/
extractPairs() {
const pairs = [];
for (let i = 0; i < this.size; i++)
pairs.push({ key: this._keys[i], value: this._values[i] });
this.clear();
return pairs;
}
/**
* Extracts the contents of this dictionary as a pair of { keys, values } arrays, and empties this dictionary.
* The array of keys is sorted according to the comparison criterion.
* The position of each value in the array of values corresponds the the position of the corresponding key in the array of keys.
* @returns a pair of { keys, values } arrays in which key[i] corresponds to value[i] in this dictionary and the keys are in sorted order.
*/
extractArrays() {
const result = { keys: this._keys, values: this._values };
this.clear();
return result;
}
/** Apply a function to each (key, value) pair in the dictionary, in sorted order.
* @param func The function to be applied.
*/
forEach(func) {
for (let i = 0; i < this.size; i++)
func(this._keys[i], this._values[i]);
}
/**
* Computes the position at which the specified key should be inserted to maintain sorted order.
* @param key The key whose position is to be computed.
* @returns an object with 'index' corresponding to the computed position and 'equal' set to true if an equivalent key already exists at that index.
*/
lowerBound(key) { return (0, SortedArray_1.lowerBound)(key, this._keys, this._compareKeys); }
}
exports.Dictionary = Dictionary;
//# sourceMappingURL=Dictionary.js.map