UNPKG

@socket-mesh/fleximap

Version:

A flexible hash map which supports deep keys.

386 lines (385 loc) 11.7 kB
function isIterable(object) { return object && (object.constructor.name === 'Object' || object instanceof Array); } function isInt(input) { return /^[0-9]+$/.test(input); } export class FlexiMap { constructor(object) { this._data = []; this.length = 0; this.defaultAsArray = (object instanceof Array); if (object) { if (isIterable(object)) { for (let i in object) { if (object.hasOwnProperty(i)) { if (isIterable(object[i])) { this._data[i] = new FlexiMap(object[i]); } else { this._data[i] = object[i]; } } } } else { this._data.push(object); } } } getLength(keyChain) { if (keyChain) { return this.count(keyChain); } else { return this._data.length; } } _getValue(key) { return this._data[key]; } _setValue(key, value) { this._data[key] = value; } _deleteValue(key) { delete this._data[key]; } getRaw(keyChain) { if (!(keyChain instanceof Array)) { keyChain = [keyChain]; } const key = keyChain[0]; const data = this._getValue(key); if (keyChain.length < 2) { return data; } else { if (data instanceof FlexiMap) { return data.getRaw(keyChain.slice(1)); } else { return undefined; } } } get(keyChain) { let result = this.getRaw(keyChain); if (result instanceof FlexiMap) { result = result.getAll(); } return result; } getRange(keyChain, fromIndex, toIndex) { const value = this.get(keyChain); let range; if (value instanceof Array) { range = []; if (toIndex == null || toIndex > value.length) { toIndex = value.length; } for (var i = fromIndex; i < toIndex; i++) { range.push(value[i]); } } else { range = {}; let recording = false; if (value instanceof Object) { for (let j in value) { if (value.hasOwnProperty(j)) { if (j === fromIndex.toString()) { recording = true; } if (recording && j === toIndex.toString()) { break; } if (recording) { range[j] = value[j]; } } } } } return range; } count(keyChain) { const elements = this.get(keyChain); if (elements) { if (isIterable(elements)) { let result = 0; for (let i in elements) { if (elements.hasOwnProperty(i)) { result++; } } return result; } return 1; } return 0; } hasImmediateKey(key) { return this._data[key] !== undefined; } hasKey(keyChain) { return (this.get(keyChain) === undefined) ? false : true; } hasType(keyChain, type) { const objects = this.get(keyChain); if (objects instanceof Object) { for (let i in objects) { if (objects.hasOwnProperty(i)) { if (objects[i] instanceof type) { return true; } } } } return false; } hasValue(keyChain, value) { const values = this.get(keyChain); if (typeof values === 'object') { for (let i in values) { if (values.hasOwnProperty(i)) { if (values[i] === value) { return true; } } } } return false; } hasObject(keyChain, object) { const objects = this.get(keyChain); if (typeof objects === 'object') { for (var i in objects) { if (objects.hasOwnProperty(i)) { if (objects[i] === object) { return true; } } } } return false; } set(keyChain, value) { const originalValue = value; if (!(keyChain instanceof Array)) { keyChain = [keyChain]; } const key = keyChain[0]; if (keyChain.length < 2) { if (!(value instanceof FlexiMap) && isIterable(value)) { value = new FlexiMap(value); } this._setValue(key, value); } else { if (!this.hasImmediateKey(key) || !(this._getValue(key) instanceof FlexiMap)) { this._setValue(key, new FlexiMap()); } this._getValue(key).set(keyChain.slice(1), value); } return originalValue; } add(keyChain, value) { if (!(keyChain instanceof Array)) { keyChain = [keyChain]; } let insertionIndex; let target = this.getRaw(keyChain); if (target == null) { insertionIndex = 0; target = new FlexiMap([value]); this.set(keyChain, target); } else if (!(target instanceof FlexiMap)) { target = new FlexiMap([target, value]); insertionIndex = target.getLength() - 1; this.set(keyChain, target); } else { insertionIndex = target.getLength(); this.set(keyChain.concat(insertionIndex.toString()), value); } return insertionIndex; } concat(keyChain, value) { if (!(keyChain instanceof Array)) { keyChain = [keyChain]; } let target = this.getRaw(keyChain); if (!isIterable(value)) { value = [value]; } if (!target) { target = new FlexiMap(value); this.set(keyChain, target); } else if (!(target instanceof FlexiMap)) { target = new FlexiMap([target].concat(value)); this.set(keyChain, target); } else { const keyChainLastIndex = keyChain.length; if (value instanceof Array) { const len = target.getLength(); keyChain = keyChain.concat(len.toString()); for (let i in value) { if (value.hasOwnProperty(i)) { this.set(keyChain, value[i]); keyChain[keyChainLastIndex] = keyChain[keyChainLastIndex] + 1; } } } else { for (let j in value) { if (value.hasOwnProperty(j)) { keyChain[keyChainLastIndex] = j; this.set(keyChain, value[j]); } } } } return value; } _remove(key) { if (this.hasImmediateKey(key)) { const data = this._getValue(key); this._deleteValue(key); if (data instanceof FlexiMap) { return data.getAll(); } return data; } return undefined; } remove(keyChain) { if (!(keyChain instanceof Array)) { keyChain = [keyChain]; } if (keyChain.length < 2) { return this._remove(keyChain[0]); } const parentMap = this.getRaw(keyChain.slice(0, -1)); if (parentMap instanceof FlexiMap) { return parentMap._remove(keyChain[keyChain.length - 1]); } return undefined; } _splice(index, count, items) { if (items) { for (var j = 0; j < items.length; j++) { if (isIterable(items[j])) { items[j] = new FlexiMap(items[j]); } } } return this._data.splice(index, count, ...items); } splice(keyChain, index, count, ...items) { const parentMap = this.getRaw(keyChain); if (parentMap instanceof FlexiMap) { const rawRemovedItems = parentMap._splice(index, count, items); const plainRemovedItems = []; let curItem; const len = rawRemovedItems.length; for (var j = 0; j < len; j++) { curItem = rawRemovedItems[j]; if (curItem instanceof FlexiMap) { plainRemovedItems.push(curItem.getAll()); } else { plainRemovedItems.push(curItem); } } return plainRemovedItems; } return []; } removeRange(keyChain, fromIndex, toIndex) { const value = this.get(keyChain); let range; if (value instanceof Array) { if (toIndex == null || toIndex > value.length) { toIndex = value.length; } range = value.splice(fromIndex, toIndex - fromIndex); this.set(keyChain, value); } else { range = {}; let deleting = false; for (let i in value) { if (value.hasOwnProperty(i)) { if (i === fromIndex.toString()) { deleting = true; } if (deleting && i === toIndex.toString()) { break; } if (deleting) { range[i] = value[i]; delete value[i]; } } } this.set(keyChain, value); } return range; } pop(keyChain) { return this.splice(keyChain, -1, 1); } removeAll() { this._data = []; } _arrayToObject(array) { const obj = {}; for (let i in array) { if (array.hasOwnProperty(i)) { obj[i] = array[i]; } } return obj; } getAll() { let isArray = this.defaultAsArray; const data = []; for (let i in this._data) { if (this._data.hasOwnProperty(i)) { const value = this._data[i]; if (value instanceof FlexiMap) { data[i] = value.getAll(); } else { data[i] = value; } } } if (isArray) { const len = data.length; for (let j = 0; j < len; j++) { if (data[j] === undefined) { isArray = false; break; } } } if (isArray) { for (let k in data) { if (data.hasOwnProperty(k)) { if (!isInt(k)) { isArray = false; break; } } } } if (isArray) { return data; } return this._arrayToObject(data); } }