File

projects/wms-framework/src/lib/baseframework/GeneralDictionary.ts

Description

Set of Hashable objects.

Index

Properties
Methods
Accessors

Properties

Private bucket
Type : KeyValue<K, V>[]
Default value : []

Methods

add
add(key: K, value: V)

Adds the given key-value into the bucket. If the bucket already has an equivalent key, the new value overwrites the previous value.

Parameters :
Name Type Optional
key K No
value V No
Returns : number

{number}

Private equalKeys
equalKeys(a: K, b: K)

If the key has an Equals() function, use it to compare keys. Otherwise, use === comparison.

Parameters :
Name Type Optional
a K No
b K No
Returns : boolean

{boolean}

find
find(key: K)

Returns the value associated with the given key. If the bucket does not contain the key, undefined is returned.

Parameters :
Name Type Optional
key K No
Returns : V

{V}

remove
remove(key: K)

Removes the given key (and it's corresponding value) from the bucket. If the bucket does not contain the key, no operation is performed.

Parameters :
Name Type Optional
key K No
Returns : boolean

{boolean}

toArray
toArray()

Returns the key-values in the bucket as an array of [key,value] entries.

Returns : []

{[K,V][]}

Accessors

keys
getkeys()

Gets all keys on the bucket.

Returns : K[]
values
getvalues()

Gets all values on the bucket.

Returns : V[]
length
getlength()

Gets the amount of entries on the bucket.

Returns : number
isEmpty
getisEmpty()

Returns true if the bucket has no entries, false otherwise.

Returns : boolean
import { Debugger } from '../diagnostics/Debugger';
import { ICollection, SimpleList } from './collections';
import { Dictionary } from './Dictionary';
import { Hashable } from './Hashable';
import { KeyValue } from './KeyValue';

/**
 * Set of `Hashable` objects.
 *
 * @class Bucket
 * @template K
 * @template V
 */
class Bucket<K extends Hashable, V> {
  private bucket: KeyValue<K, V>[] = [];

  /**
   * Adds the given key-value into the bucket. If the bucket already has
   * an equivalent key, the new value overwrites the previous value.
   *
   * @param {K} key
   * @param {V} value
   * @return {*}  {number}
   * @memberof Bucket
   */
  add(key: K, value: V): number {
    const prevLength = this.length;
    this.remove(key); // Avoid duplicate keys.
    this.bucket.push(new KeyValue(key, value));
    return this.length - prevLength;
  }

  /**
   * Removes the given key (and it's corresponding value) from the bucket.
   * If the bucket does not contain the key, no operation is performed.
   *
   * @param {K} key
   * @return {*}  {boolean}
   * @memberof Bucket
   */
  remove(key: K): boolean {
    const idx = this.bucket.findIndex((x) => this.equalKeys(key, x.Key));
    if (idx !== -1) {
      this.bucket.splice(idx, 1);
      return true;
    }
    return false;
  }

  /**
   * Returns the value associated with the given key. If the bucket does not
   * contain the key, `undefined` is returned.
   *
   * @param {K} key
   * @return {*}  {V}
   * @memberof Bucket
   */
  find(key: K): V {
    return this.bucket.find((x) => this.equalKeys(key, x.Key))?.Value;
  }

  /**
   * Returns the key-values in the bucket as an array of `[key,value]` entries.
   *
   * @return {*}  {[K,V][]}
   * @memberof Bucket
   */
  toArray(): [K, V][] {
    return this.bucket as unknown as [K, V][];
  }

  /**
   * Gets all keys on the bucket.
   *
   * @readonly
   * @type {K[]}
   * @memberof Bucket
   */
  get keys(): K[] {
    return this.bucket.map((x) => x.Key);
  }

  /**
   * Gets all values on the bucket.
   *
   * @readonly
   * @type {V[]}
   * @memberof Bucket
   */
  get values(): V[] {
    return this.bucket.map((x) => x.Value);
  }

  /**
   * Gets the amount of entries on the bucket.
   *
   * @readonly
   * @type {number}
   * @memberof Bucket
   */
  get length(): number {
    return this.bucket.length;
  }

  /**
   * Returns `true` if the bucket has no entries,
   * `false` otherwise.
   *
   * @readonly
   * @type {boolean}
   * @memberof Bucket
   */
  get isEmpty(): boolean {
    return this.length === 0;
  }

  /**
   * If the key has an `Equals()` function, use it to compare keys.
   * Otherwise, use `===` comparison.
   *
   * @private
   * @param {K} a
   * @param {K} b
   * @return {*}  {boolean}
   * @memberof Bucket
   */
  private equalKeys(a: K, b: K): boolean {
    return typeof a.Equals === 'function' ? a.Equals(b) : a === b;
  }
}

/**
 * A dictionary to store `Hashable` keys.
 *
 * @export
 * @class GeneralDictionary
 * @extends {Dictionary<K, V>}
 * @template K
 * @template V
 */
export class GeneralDictionary<K extends Hashable, V> extends Dictionary<K, V> {
  /**
   * A map from hash values to buckets.
   *
   * @private
   * @type {Map<number, Bucket<K,V>>}
   * @memberof GeneralDictionary
   */
  private internalMap: Map<number, Bucket<K, V>> = new Map<
    number,
    Bucket<K, V>
  >();

  /**
   * Count of entries on this dictionary.
   *
   * @private
   * @memberof GeneralDictionary
   */
  private internalCount = 0;

  /**
   * Gets the value associated with the given key.
   * If the key is not present on the dictionay, `null` is returned.
   *
   * @param {K} key
   * @return {*}  {V}
   * @memberof GeneralDictionary
   */
  getItem(key: K): V {
    const bucket = this.getBucket(key);
    const found = bucket?.find(key);
    return found ?? null;
  }

  /**
   * Inserts the given key-value into the dictionary.
   * If the dictionary already has an equivalent key, the associated
   * value is replaced with the given (new) value.
   *
   * @param {K} key
   * @param {V} value
   * @memberof GeneralDictionary
   */
  setItem(key: K, value: V): void {
    const hash = this.getHash(key);
    let bucket = this.getBucketByHash(hash);
    if (bucket == null) {
      bucket = new Bucket();
      this.internalMap.set(hash, bucket);
    }
    this.internalCount += bucket.add(key, value);
  }

  /**
   * Inserts the given key-value into the dictionary.
   * If the dictionary already has an equivalent key, the associated
   * value is replaced with the given (new) value.
   *
   * @param {K} key
   * @param {V} value
   * @memberof GeneralDictionary
   */
  addEntry(key: K, value: V): void {
    this.setItem(key, value);
  }

  /**
   * Returns `true` if there is an equivalent key in the dictionary,
   * `false` otherwise.
   *
   * @param {K} key
   * @return {*}  {boolean}
   * @memberof GeneralDictionary
   */
  hasKey(key: K): boolean {
    const foundValue = this.getBucket(key)?.find(key);
    return foundValue != null;
  }

  /**
   * Removes the given key (and it's associated value) from the dictionary.
   * If there is not an equivalent key in the dictionary, no operation
   * is performed.
   *
   * @param {K} key
   * @memberof GeneralDictionary
   */
  removeEntry(key: K): void {
    const hash = this.getHash(key);
    const bucket = this.getBucketByHash(hash);
    if (bucket?.remove(key)) {
      this.internalCount--;
      if (bucket.isEmpty) {
        this.internalMap.delete(hash);
      }
    }
  }

  /**
   * Attempts to obtain the value associated with the given key.
   * If the key exists on the dictionary, `value()` is called with the key's
   * value as parameter and `true` is returned.
   * If the key is not present on the dictionary, `value()` is not called
   * and `false` is returned.
   *
   * @param {K} key
   * @param {(v: V) => void} value
   * @return {*}  {boolean}
   * @memberof GeneralDictionary
   */
  tryGetValue(key: K, value: (v: V) => void): boolean {
    const foundValue = this.getBucket(key)?.find(key);
    if (foundValue != null) {
      value(foundValue);
      return true;
    }
    return false;
  }

  /**
   * Returns `true` if there is an equivalent key in the dictionary,
   * `false` otherwise.
   *
   * @param {K} key
   * @return {*}  {boolean}
   * @memberof GeneralDictionary
   */
  containsKey(key: K): boolean {
    return this.hasKey(key);
  }

  /**
   * Inserts the given key-value into the dictionary.
   * If the dictionary already has an equivalent key, the associated
   * value is replaced with the given (new) value.
   *
   * @param {[K, V]} value
   * @memberof GeneralDictionary
   */
  add(value: [K, V]): void {
    this.setItem(value[0], value[1]);
  }

  /**
   * Removes all entries from the dictionary.
   *
   * @memberof GeneralDictionary
   */
  clear(): void {
    this.internalMap.clear();
    this.internalCount = 0;
  }

  contains(value: [K, V]): boolean {
    Debugger.Throw('Method not implemented.');
    return false;
  }

  remove(value: [K, V]): boolean {
    Debugger.Throw('Method not implemented.');
    return false;
  }

  copyTo(target: [K, V][], index: number): void {
    Debugger.Throw('Method not implemented.');
  }

  [Symbol.iterator](): Iterator<[K, V], any, undefined> {
    return this.internalArray[Symbol.iterator]();
  }

  /**
   * Gets an array with all entries on the dictionary.
   *
   * @readonly
   * @type {[K, V][]}
   * @memberof GeneralDictionary
   */
  get internalArray(): [K, V][] {
    const arr = [];
    for (const bucket of this.internalMap.values()) {
      arr.push(...bucket.toArray());
    }
    return arr;
  }

  /**
   * Gets the number of entries on the dictionary.
   *
   * @readonly
   * @type {number}
   * @memberof GeneralDictionary
   */
  get count(): number {
    return this.internalCount;
  }

  /**
   * Gets all keys on the dictionary.
   *
   * @readonly
   * @type {ICollection<K>}
   * @memberof GeneralDictionary
   */
  get keys(): ICollection<K> {
    const keys: K[] = [];
    for (const bucket of this.internalMap.values()) {
      keys.push(...bucket.keys);
    }
    return new SimpleList(keys);
  }

  /**
   * Gets all values on the dictionary.
   *
   * @readonly
   * @type {ICollection<V>}
   * @memberof GeneralDictionary
   */
  get values(): ICollection<V> {
    const values: V[] = [];
    for (const bucket of this.internalMap.values()) {
      values.push(...bucket.values);
    }
    return new SimpleList(values);
  }

  /**
   * Gets the bucket associated with the given key.
   *
   * @private
   * @param {K} key
   * @return {*}  {Bucket<K,V>}
   * @memberof GeneralDictionary
   */
  private getBucket(key: K): Bucket<K, V> {
    return this.getBucketByHash(this.getHash(key));
  }

  /**
   * Gets the bucket associated with the given hash.
   *
   * @private
   * @param {number} hash
   * @return {*}  {Bucket<K,V>}
   * @memberof GeneralDictionary
   */
  private getBucketByHash(hash: number): Bucket<K, V> {
    return this.internalMap.get(hash);
  }

  /**
   * If the key has a `GetHashCode()` function, use it to obtain the hash.
   * Otherwise, return a defaul hash value.
   *
   * @private
   * @param {K} key
   * @return {*}  {number}
   * @memberof GeneralDictionary
   */
  private getHash(key: K): number {
    return typeof key.GetHashCode === 'function' ? key.GetHashCode() : 4373;
    // NOTE: The default hash is a prime number (4373) for
    //       no particular reason. Any number should do.
  }
}

result-matching ""

    No results matching ""