# All files / util obj_map.ts

100% Statements 52/52
100% Branches 14/14
100% Functions 9/9
100% Lines 45/45

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111

    2x

    2x

    1762x

    1762x

    2x
    4236x
    4236x
    4236x
    1374x

    2871x
    2871x
    2846x

    16x

    2x
    1060x

    2x
    1568x
    1568x
    1568x
    1322x
    1322x

    246x
    247x
    239x
    239x

    7x

    2x
    762x
    762x
    762x
    1x

    761x
    763x
    760x
    753x

    7x

    760x

    1x

    2x
    3432x
    2307x
    2307x

    2x
    58x

    2x
     /**
     * Copyright 2017 Google Inc.
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *   http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */

    import { Equatable } from './misc';
    import * as objUtil from './obj';

    type Entry<K, V> = [K, V];

    /**
     * A map implementation that uses objects as keys. Objects must implement the
     * Equatable interface and must be immutable. Entries in the map are stored
     * together with the key being produced from the mapKeyFn. This map
     * automatically handles collisions of keys.
     */
    export class ObjectMap<KeyType extends Equatable<KeyType>, ValueType> {
      /**
       * The inner map for a key -> value pair. Due to the possibility of
       * collisions we keep a list of entries that we do a linear search through
       * to find an actual match. Note that collisions should be rare, so we still
       * expect near constant time lookups in practice.
       */
      private inner: {
        [canonicalId: string]: Array<Entry<KeyType, ValueType>>;
      } = {};

      constructor(private mapKeyFn: (key: KeyType) => string) {}

      /** Get a value for this key, or undefined if it does not exist. */
      get(key: KeyType): ValueType | undefined {
        const id = this.mapKeyFn(key);
        const matches = this.inner[id];
        if (matches === undefined) {
          return undefined;
        }
        for (const [otherKey, value] of matches) {
          if (otherKey.isEqual(key)) {
            return value;
          }
        }
        return undefined;
      }

      has(key: KeyType): boolean {
        return this.get(key) !== undefined;
      }

      /** Put this key and value in the map. */
      set(key: KeyType, value: ValueType): void {
        const id = this.mapKeyFn(key);
        const matches = this.inner[id];
        if (matches === undefined) {
          this.inner[id] = [[key, value]];
          return;
        }
        for (let i = 0; i < matches.length; i++) {
          if (matches[i][0].isEqual(key)) {
            matches[i] = [key, value];
            return;
          }
        }
        matches.push([key, value]);
      }

      /**
       * Remove this key from the map. Returns a boolean if anything was deleted.
       */
      delete(key: KeyType): boolean {
        const id = this.mapKeyFn(key);
        const matches = this.inner[id];
        if (matches === undefined) {
          return false;
        }
        for (let i = 0; i < matches.length; i++) {
          if (matches[i][0].isEqual(key)) {
            if (matches.length === 1) {
              delete this.inner[id];
            } else {
              matches.splice(i, 1);
            }
            return true;
          }
        }
        return false;
      }

      forEach(fn: (key: KeyType, val: ValueType) => void): void {
        objUtil.forEach(this.inner, (_, entries) => {
          for (const [k, v] of entries) {
            fn(k, v);
          }
        });
      }

      isEmpty(): boolean {
        return objUtil.isEmpty(this.inner);
      }
    }