How to implement TwoWayWeakMap? Using WeakRef?

The idea is that TwoWayWeakMap would work like WeakMap, except that you would be able to get key objects by their value. For example, here is how the usage would look like:

class TwoWayWeakMap {
  // What goes here?
}

class SomeClass {}

const map = new TwoWayWeakMap<SomeClass, number>()

function main() {
  const o = new SomeClass

  map.set(o, 42)

  console.log(map.get(o)) // logs "42"
  console.log(map.keyFrom(42)) // logs "SomeClass {}" (the `o` object)
}

main()

Once main() completes, I would expect the o key to be collected at some point when it is no longer referenced.

Calling keyFrom with a value that was mapped from the object would return undefined, which would be similar to WeakRef returning undefined after an object has been collected.

I gave it a shot using WeakRef, but it doesn't seem that the object is ever collected. Maybe I missed something silly. If the following example were to work as I intended, then eventually it would log "o was collected!", but you'll see in your console that the object persists, even after forcing garbage collection in Chrome's devtools:

console.log keeps the object alive. Strangely enough, even if you only print it once, then clear the console. Adjust these two lines so that console.log doesn't get hold of the object:

console.log(`o still exists? (${tick++})`, !!obj);
...
console.log(!!map.keyFrom(42));
2 Likes

I think there's also a bug/typo in this part:

for (const ref of this.#refs) {
    const o = ref.deref();
    if (!o) {
        this.#refs.delete(ref);
        return undefined; // <<< I don't think returning here is correct, the ref could have been for a different key/value pair
    }
    if (this.get(o) === v) return o;
}

Maybe this:

for (const ref of this.#refs) {
    const o = ref.deref();
    if (o === undefined) {
        this.#refs.delete(ref);
    } else {
        if (this.get(o) === v) return o;
    }
}
1 Like