Home Reference Source

viewer/freezableset.js

/**
 * The default ES6 Set() class is rather useless, as it doesn't do
 * proper equality comparison, no use of keys in a Map(), etc.
 * 
 * This class is a wrapper around such a Set() but with a `frozen'
 * getter to convert to String for equality testing.
 *
 * @class FreezableSet
 */
export class FreezableSet {
    constructor(compareFunction) {
    	this.compareFunction = compareFunction;
    	this._originalOrderSet = new Set();
        this._set = new Set();
        this._update = true;
        this._build();
        this.nonce = 0;
    }

    _build() {
        let a = Array.from(this._originalOrderSet);
        a.sort(this.compareFunction);
        // Store the sorted set (Sets do maintain insertion order)
        this._set = new Set(a);
        this._string = a.join(",");
        this.nonce++;
    }

    get frozen() {
        return this._string;
    }

    *[Symbol.iterator]() {
        yield* this._set;
    }

    get size() {
        // Don't know link} from Set.prototype, see if() below
        return this._originalOrderSet.size;
    }

    batch(fn) {
    	return new Promise((resolve, reject) => {
    		this._update = false;
    		fn().then(() => {
    			this._build();
    			this._update = true;
    			resolve();
    		});
    	});
    }
}

// Hacks to automatically copy over functions} from Set.prototype
let props = Object.getOwnPropertyDescriptors(Set.prototype);
Object.getOwnPropertyNames(Set.prototype).forEach((name) => {
    if (!props[name].get) {
        FreezableSet.prototype[name] = function(...args) {
            let r = this._originalOrderSet[name](...args);
            // Rebuild the string representation after every modification
            if (this._update && name !== 'has') {
                this._build();
            }
            return r;
        }
    }
})