forEach (v5)

Revision 5 of this benchmark created on


Setup

class DeepMapO {
  map = {};
  keyMap = {};

  constructor(serialize, initialValues) {
    this.serialize = serialize;
    if (initialValues?.[Symbol.iterator]) {
      for (const [key, value] of initialValues) this.set(key, value);
    }
  }

  set(key, value) {
    const keyStr = this.serialize(key);
    this.keyMap[keyStr] = key;
    this.map[keyStr] = value;
    return this;
  }

  get(key) {
    return this.map[this.serialize(key)];
  }

  has(key) {
    return this.map.hasOwnProperty(this.serialize(key));
  }

  delete(key) {
    const keyStr = this.serialize(key);
    const hasKey = this.map.hasOwnProperty(keyStr);
    delete this.keyMap[hasKey];
    delete this.map[keyStr];
    return hasKey;
  }

  clear() {
    this.map = {};
    this.keyMap = {};
  }

  get size() {
    return Object.keys(this.map).length;
  }

  *keys() {
    for (let keyStr in this.keyMap) yield this.keyMap[keyStr];
  }

  *values() {
    for (let keyStr in this.keyMap) yield this.map[keyStr];
  }

  *entries() {
    for (let keyStr in this.keyMap) yield [this.keyMap[keyStr], this.map[keyStr]];
  }

  forEach(cb, thisArg) {
    for (let keyStr in this.keyMap) cb.call(thisArg, this.map[keyStr], this.keyMap[keyStr], this);
  }

  copy() {
    const newMap = new DeepMapO(this.serialize);
    // directly copying the map and keymap (instead of passing it to the constructor), avoids having to recalculate each key hash
    newMap.map = { ...this.map };
    newMap.keyMap = { ...this.keyMap };
    return newMap;
  }

  [Symbol.iterator]() {
    return this.entries();
  }
}

class DeepMapSO {
  map = {};
  _size = 0;

  constructor(serialize, initialValues) {
    this.serialize = serialize;
    if (initialValues?.[Symbol.iterator]) {
      for (const [key, value] of initialValues) this.set(key, value);
    }
  }

  set(key, value) {
    const keyStr = this.serialize(key);
    this.map[keyStr] = [key, value];
    this._size++;
    return this;
  }

  get(key) {
    return this.map[this.serialize(key)]?.[1];
  }

  has(key) {
    return this.map.hasOwnProperty(this.serialize(key));
  }

  delete(key) {
    const keyStr = this.serialize(key);
    const hasKey = this.map.hasOwnProperty(keyStr);
    delete this.map[keyStr];
    if (hasKey) this._size--;
    return hasKey;
  }

  clear() {
    this.map = {};
    this._size = 0;
  }

  get size() {
    return this._size;
  }

  *keys() {
    for (let keyStr in this.map) yield this.map[keyStr][0];
  }

  *values() {
    for (let keyStr in this.map) yield this.map[keyStr][1];
  }

  *entries() {
    for (let keyStr in this.map) yield this.map[keyStr];
  }

  forEach(cb, thisArg) {
    for (let keyStr in this.map) cb.call(thisArg, this.map[keyStr][1], this.map[keyStr][0], this);
  }

  copy() {
    const newMap = new DeepMapSO(this.serialize);
    // directly copying the map and map (instead of passing it to the constructor), avoids having to recalculate each key hash
    newMap.map = { ...this.map };
    newMap._size = this._size;
    return newMap;
  }

  [Symbol.iterator]() {
    return this.entries();
  }
}

    const serialize = k => `${k.chain}}_{${k.joint}`;
    const deSerialize = str => {
      const parts = str.split('}_{');
      return { chain: parts[0], joint: parts[1] };
    };

    const map = new Map();
    const obj = {};
    const deepMapObj = new DeepMapO(serialize);
	const deepMapSingleObj = new DeepMapSO(serialize);

    for (let chain = 0; chain < 5; chain++) {
      for (let joint = 0; joint < 10; joint++) {
        const key = {
          chain: `cf221f55-957c-43bf-be45-f5be6d2cebe4-${chain}`,
          joint: `dc7d8abf-871c-4980-84ed-c7049a67558d-${joint}`
        };
        const value = `value-${chain}-${joint}`;
        map.set(serialize(key), value);
        obj[serialize(key)] = value;
    	deepMapObj.set(key, value);
    	deepMapSingleObj.set(key, value);
      }
    }

    const testKey = {
      chain: `cf221f55-957c-43bf-be45-f5be6d2cebe4-${4}`,
      joint: `dc7d8abf-871c-4980-84ed-c7049a67558d-${8}`
    };

Test runner

Ready to run.

Testing in
TestOps/sec
native map
let k, v;
map.forEach((value, key) => {
  k = deSerialize(key);
  v = value;
});
ready
native object
let k, v;
for (let key in obj) {
  k = deSerialize(key);
  v = obj[key];
}
ready
deepMap obj
let k, v;
deepMapObj.forEach((value, key) => {
  k = key;
  v = value;
});
ready
deepMap single obj
let k, v;
deepMapSingleObj.forEach((value, key) => {
  k = key;
  v = value;
});
ready

Revisions

You can edit these tests or add more tests to this page by appending /edit to the URL.