File

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

Description

A dictionary to store Hashable keys.

Extends

Dictionary

Index

Properties
Methods
Accessors

Properties

Private internalCount
Type : number
Default value : 0

Count of entries on this dictionary.

Private internalMap
Type : Map<number | Bucket<K, V>>
Default value : new Map< number, Bucket<K, V> >()

A map from hash values to buckets.

Abstract count
Type : number
Inherited from Dictionary
Defined in Dictionary:61

Gets the count of elements in the dictionary

Abstract internalArray
Type : []
Inherited from Dictionary
Defined in Dictionary:62
Abstract keys
Type : ICollection<K>
Inherited from Dictionary
Defined in Dictionary:41

The keys collection

Abstract values
Type : ICollection<V>
Inherited from Dictionary
Defined in Dictionary:51

The values collection

Methods

add
add(value: [K, V])
Inherited from Dictionary
Defined in Dictionary:299

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.

Parameters :
Name Type Optional
value [K, V] No
Returns : void
addEntry
addEntry(key: K, value: V)
Inherited from Dictionary
Defined in Dictionary:222

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.

Parameters :
Name Type Optional
key K No
value V No
Returns : void
clear
clear()
Inherited from Dictionary
Defined in Dictionary:308

Removes all entries from the dictionary.

Returns : void
contains
contains(value: [K, V])
Inherited from Dictionary
Defined in Dictionary:313
Parameters :
Name Type Optional
value [K, V] No
Returns : boolean
containsKey
containsKey(key: K)
Inherited from Dictionary
Defined in Dictionary:287

Returns true if there is an equivalent key in the dictionary, false otherwise.

Parameters :
Name Type Optional
key K No
Returns : boolean

{boolean}

copyTo
copyTo(target: [], index: number)
Inherited from Dictionary
Defined in Dictionary:323
Parameters :
Name Type Optional
target [] No
index number No
Returns : void
Private getBucket
getBucket(key: K)

Gets the bucket associated with the given key.

Parameters :
Name Type Optional
key K No
Returns : Bucket<K, V>

{Bucket<K,V>}

Private getBucketByHash
getBucketByHash(hash: number)

Gets the bucket associated with the given hash.

Parameters :
Name Type Optional
hash number No
Returns : Bucket<K, V>

{Bucket<K,V>}

Private getHash
getHash(key: K)

If the key has a GetHashCode() function, use it to obtain the hash. Otherwise, return a defaul hash value.

Parameters :
Name Type Optional
key K No
Returns : number

{number}

getItem
getItem(key: K)
Inherited from Dictionary
Defined in Dictionary:188

Gets the value associated with the given key. If the key is not present on the dictionay, null is returned.

Parameters :
Name Type Optional
key K No
Returns : V

{V}

hasKey
hasKey(key: K)
Inherited from Dictionary
Defined in Dictionary:234

Returns true if there is an equivalent key in the dictionary, false otherwise.

Parameters :
Name Type Optional
key K No
Returns : boolean

{boolean}

remove
remove(value: [K, V])
Inherited from Dictionary
Defined in Dictionary:318
Parameters :
Name Type Optional
value [K, V] No
Returns : boolean
removeEntry
removeEntry(key: K)
Inherited from Dictionary
Defined in Dictionary:247

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.

Parameters :
Name Type Optional
key K No
Returns : void
setItem
setItem(key: K, value: V)
Inherited from Dictionary
Defined in Dictionary:203

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.

Parameters :
Name Type Optional
key K No
value V No
Returns : void
tryGetValue
tryGetValue(key: K, value: (v: V) => void)
Inherited from Dictionary
Defined in Dictionary:270

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.

Parameters :
Name Type Optional
key K No
value function No
Returns : boolean

{boolean}

()
Inherited from Dictionary
Defined in Dictionary:327
Returns : Iterator<, any, undefined>

Accessors

internalArray
getinternalArray()

Gets an array with all entries on the dictionary.

Returns : []
count
getcount()

Gets the number of entries on the dictionary.

Returns : number
keys
getkeys()

Gets all keys on the dictionary.

Returns : ICollection<K>
values
getvalues()

Gets all values on the dictionary.

Returns : ICollection<V>
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 ""