Skip to content

Instantly share code, notes, and snippets.

@tschneidereit
Created November 3, 2013 21:15
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tschneidereit/7294906 to your computer and use it in GitHub Desktop.
Save tschneidereit/7294906 to your computer and use it in GitHub Desktop.
Iterable WeakMap implemented using Weak References
function IterableWeakKeyMap() {
const keyMap = new WeakMap();
const refValueMap = new Map();
return Object.freeze({
get: function(key) {
const entry = keyMap.get(key);
return entry && entry.value;
},
set: function(key, value) {
const ref = makeWeakRef(key);
keyMap.set(key, {value: value, ref: ref});
refValueMap.set(ref, value);
ref.register(function() {
refValueMap.delete(ref);
});
},
delete: function(key) {
const entry = keyMap.get(key);
if (!entry) {
return;
}
keyMap.delete(key);
refValueMap.delete(entry.ref);
},
iterator: function() {
for (const item of refValueMap) {
const key = item[0].get();
if (key) {
yield [key, item[1]];
}
}
},
items: function() {
return this.iterator();
},
keys: function() {
for (const ref of refValueMap.keys()) {
const key = ref.get();
if (key) {
yield key;
}
}
},
values: function() {
// Not sure if we also need to filter these
return refValueMap.values();
}
});
}
@gsathya
Copy link

gsathya commented Apr 1, 2019

Updated using the new WeakRef API (+ESNext sauce):

class IterableWeakMap {
  #weakMap = new WeakMap();
  #refMap = new Map();
  #finalizationGroup = new FinalizationGroup(IterableWeakMap.#cleanup);

  static #cleanup(iterator) {
    for (const { map, ref } of iterator) {
      map.delete(ref);
    }
  }

  set(key, value) {
    const ref = new WeakRef(key);

    this.#weakMap.set(key, { value, ref });
    this.#refMap.set(ref, value);
    finalizationGroup.register(key, { map: this.#weakRefMap, ref }, ref);
  }

  get(key) {
    const entry = this.#weakMap.get(key);
    return entry && entry.value;
  }

  delete(key) {
    const entry = this.#weakMap.get(key);
    if (!entry) {
      return;
    }

    this.#weakMap.delete(key);
    this.#refMap.delete(entry.ref);
    this.#finalizationGroup.unregister(entry.ref);
  }

  *iterator() {
    for (const [ref, value] of this.#refMap) {
      const key = ref.get();
      if (key) yield [key, value];
    }
  }

  items() {
    return this.iterator();
  }

  *keys() {
    for (const [ref] of this.#refMap) {
      const key = ref.get();
      if (key) yield key;
    }
  }

  values() {
    return this.#refMap.values();
  }
}

@petternordholm
Copy link

A warning if using the code above. There is a bug in the set method if the same key is used multiple times. This will cause multiple weak references for the same key in the #refMap. A simple solution is to call this.delete(ref) after creating the WeakRef .

map = new IterableWeakMap():
const key1 = Symbol('1');

map.set(key1, 'TEST_1');
map.set(key1, 'TEST_1_1');

map.values() // Returns ['TEST_1', 'TEST_1_1']

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment