UNPKG

oset

Version:

Ordered Set Data Structure

206 lines (188 loc) 5.98 kB
/* ** OSet -- Ordered Set Data Structure ** Copyright (c) 2015-2023 Dr. Ralf S. Engelschall <rse@engelschall.com> ** ** Permission is hereby granted, free of charge, to any person obtaining ** a copy of this software and associated documentation files (the ** "Software"), to deal in the Software without restriction, including ** without limitation the rights to use, copy, modify, merge, publish, ** distribute, sublicense, and/or sell copies of the Software, and to ** permit persons to whom the Software is furnished to do so, subject to ** the following conditions: ** ** The above copyright notice and this permission notice shall be included ** in all copies or substantial portions of the Software. ** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. ** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY ** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, ** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE ** SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ export default class OMap { /* create data structure instance */ constructor () { this._items = 0 this._index = {} this._ring = {} this._ring.prev = this._ring this._ring.next = this._ring return this } /* get number of items */ size () { return this._items } /* get keys of all items in order */ keys () { return this.each(function (val, key) { this.push(key) }, []) } /* get values of all items in order */ values () { return this.each(function (val /*, key */) { this.push(val) }, []) } /* find values of all items in order matching a predicate */ find (predicate, ctx) { if (arguments < 2) ctx = this return this.each(function (val, key, order) { if (predicate.call(ctx, val, key, order)) this.push(val) }, []) } /* iterate over all items in order */ each (iterator, ctx) { if (arguments < 2) ctx = this let i = 0 let bucket = this._ring.next while (bucket !== this._ring) { iterator.call(ctx, bucket.val, bucket.key, i++) bucket = bucket.next } return ctx } /* check whether item exists under key */ has (key) { const bucket = this._index[key] return (bucket !== undefined) } /* get value under key */ get (key) { const bucket = this._index[key] if (bucket === undefined) return undefined return bucket.val } /* set value under key */ set (key, val, toFront) { let bucket = this._index[key] if (bucket === undefined) { /* insert new bucket */ bucket = { key, val } this._index[key] = bucket if (toFront) { bucket.next = this._ring.next bucket.prev = this._ring bucket.next.prev = bucket this._ring.next = bucket } else { bucket.prev = this._ring.prev bucket.next = this._ring bucket.prev.next = bucket this._ring.prev = bucket } this._items++ } else { /* replace existing bucket */ bucket.val = val } return this } /* delete item under key */ del (key) { const bucket = this._index[key] if (bucket === undefined) throw new Error("del: no such item") delete this._index[key] bucket.prev.next = bucket.next bucket.next.prev = bucket.prev delete bucket.prev delete bucket.next this._items-- return this } /* delete all items */ clear () { while (this._items > 0) this.del(this._ring.next.key) return this } /* merge with other map */ merge (other) { other.each((val, key) => { this.set(key, val) }) other.clear() return this } /* create new map based on union with other map */ union (other) { const result = new OMap() this.each((val, key) => { result.set(key, val) }) other.each((val, key) => { result.set(key, val) }) return result } /* create new map based on intersection with other map */ intersection (other) { const result = new OMap() this.each((val, key) => { if (other.has(key)) { if (other.get(key) !== val) throw new Error("intersect: different values under key \"" + key + "\"") result.set(key, val) } }) return result } /* create new map based on difference with other map */ difference (other) { const result = new OMap() this.each((val, key) => { if (!other.has(key)) result.set(key, val) }) return result } /* sort map in-place (comparison by key) */ sort (compare) { if (typeof compare !== "function") { compare = (a, b) => { if (a < b) return -1 else if (a > b) return +1 else return 0 } } const keyvals = this.each(function (val, key) { this.push({ key, val }) }, []) keyvals.sort((a, b) => compare(a.key, b.key)) this.clear() keyvals.forEach((keyval) => { this.set(keyval.key, keyval.val) }) return this } }