Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /*
- * Store is a JSON object that holds no references (pure data only).
- * Nodes can be locked and unlocked to aid in data not being accidentally changed.
- * Nodes can be added and deleted and any parent nodes are added / cleaned up automatically.
- *
- * create store...
- * let store = storeCreator({});
- *
- *
- * store.set('lvl1/lvl2', 'hello') // Updates the node and adds any parents if missing
- * store.set('lvl1/lvl2', undefined) // Deletes the node and any parent nodes without any children (undefined / null / [] / {}) all result in delete
- * store.get(); // gets the full contents of store
- * store.get('lvl1'); // gets the contents of store at a particular node
- * store.lock(); // Restricts any changes to the store
- * store.unlock(); // Removes the lock
- *
- */
- // TODO: FINISH UPDATE
- let state = storeCreator({});
- state.push('l1',[
- {cat: 'red', val: 'a'},
- {cat: 'blue', val: 'b'},
- {cat: 'red', val: 'c'},
- {cat: 'red', val: 'd'},
- {cat: 'red', val: 0}
- ]);
- state.unshift('l1',[{cat: 'red', val: 0}, {cat: 'red', val: 0}]);
- state.apply('l1', pushArray => pushArray.pop());
- let result = JSON.stringify(state.get('l1', {
- limit: 6, meta: true
- }), null, 2);
- // print result
- document.querySelector('#result')
- .innerHTML = result;
- function storeCreator(obj) {
- let store = cloner(obj);
- let locked = false;
- let idCount = 1;
- return {
- get: (key, options) => get(key, options, store),
- set: (key, val) => {
- if (locked) {
- console.log('This Store is locked. Unlock to perform set operations');
- } else {
- store = iu(store, key, val);
- }
- },
- push: (key, val) => {
- if (locked) {
- console.log('This Store is locked. Unlock to perform push operations');
- } else {
- const valArray = isArray(val)? val : [val];
- const rows = valArray.reduce((ac,cv) => {
- ac.push({id: idCount, value: cv});
- idCount++;
- return ac;
- }, get(key, undefined, store) || []);
- store = iu(store, key, rows);
- }
- },
- unshift: (key, val) => {
- if (locked) {
- console.log('This Store is locked. Unlock to perform push operations');
- } else {
- const valArray = isArray(val)? val : [val];
- const rows = valArray.reduce((ac,cv) => {
- ac.unshift({id: idCount, value: cv});
- idCount++;
- return ac;
- }, get(key, undefined, store) || []);
- store = iu(store, key, rows);
- }
- },
- apply(key, func) {
- const pushArray = get(key, undefined, store);
- func(pushArray);
- },
- lock: () => {
- locked = true;
- },
- unlock: () => {
- locked = false;
- }
- };
- }
- function isArray(val) {
- return typeof val === 'object' && Array.isArray(val);
- }
- function get (key, options, store) {
- if (!key && !options) { return store; }
- if (!key && options) { return queryResult(store, options); }
- else if (key) {
- const subArray = keyToArray(key, store);
- const result = subArray.reduce((ac, cv, i, arr) => {
- return ac[cv];
- }, store);
- if (!options) { return result } else {
- return queryResult(result, options);
- }
- }
- }
- function queryResult(obj, options) {
- /*
- * queries are expected to be performed on Map objects - not arrays... see readme
- *
- * options
- * - filter: fn => boolean: Function that returns boolean for each result
- * - limit: number: Limits the final result set to the specified limit
- * - sort: Array<[string, asc | desc]>: Sorts the result set by this key
- */
- obj = filterResults(obj, options.filter);
- obj = sortResults(obj, options.sort);
- obj = limitResults(obj, options.limit);
- if (!options.meta) {
- return Object.keys(obj).map(key => obj[key].value);
- } else { return obj; }
- }
- function filterResults(obj, filter) {
- if(!filter) { return Object.keys(obj).map(key => obj[key]); }
- else {
- return Object
- .keys(obj) // get keys as array
- .map(key => obj[key]) // get the values as array
- .filter(item => filter(item)) // apply the filter function to each item in array
- }
- }
- function sortResults(obj, sort) {
- if (!sort) { return obj } else {
- const sortObjArray = isArray(sort) ? sort : [sort];
- const values = numberSort(objToValueArray(obj), sortObjArray);
- return values;
- }
- }
- function objToValueArray(obj) {
- return Object.keys(obj).map(key => obj[key]);
- }
- function limitResults(obj, limit) {
- if (!limit) { return obj } else {
- const newKeys = Object.keys(obj);
- if (limit < newKeys.length ) { newKeys.length = limit }
- return newKeys.map(key => obj[key]);
- }
- }
- function iu(state, subArray, val) {
- subArray = keyToArray(subArray, state);
- return reducer(state, subArray, cloner(val), 0);
- }
- function keyToArray(subArray, state) {
- try {
- if(!subArray) { throw 'No target provided'; } else
- if(subArray.length < 1) { throw 'No target provided'; } else
- if(!state) { throw 'No state object provided'; }
- return subArray.split('/').filter(segment => segment);
- } catch(error) { console.error('Error in reading keys', error)}
- }
- function reducer(_state, subArray, val, l) {
- try {
- const key = subArray[l];
- if(l+1 === subArray.length) {
- const value = val;
- const replacer =
- val === undefined ||
- val === null ||
- typeof val === 'object' && Array.isArray(val) && val.length === 0 ||
- typeof val === 'object' && !Array.isArray(val) && Object.keys(val).length === 0 ? undefined : val;
- return Object.assign({}, _state, { [key]: replacer });
- }
- else {
- const value = _state[key] ? _state[key] : {};
- return Object.assign({}, _state, {[key]: reducer(value, subArray, val, l+1)} );
- }
- } catch(error) { console.error('reducer error =>', error);}
- }
- function cloner(arg) {
- return JSON.parse(JSON.stringify(arg));
- }
- function checkLocks(locks, keyToFind) {
- const lockKeys = Object.keys(locks);
- if(lockKeys.some(lockKey => keyToFind.match(lockKey))) {
- throw 'This node or parent node is locked'
- }
- }
- // array sort functions
- function numberSort(arr, sortArr, direction) {
- let sortLevel = 0;
- const result = arr.reduce((ac, cv, i, arr) => {
- if(!ac.old) { ac.old = arr }
- const targetItem = sortArr[sortLevel].dir && sortArr[sortLevel].dir === 'desc' ?
- largest(ac.old, sortArr, sortLevel)
- :
- smallest(ac.old, sortArr, sortLevel);
- const newArray = [...ac.new, targetItem];
- const oldArray = ac.old.filter(item => item !== targetItem);
- return { old: oldArray, new: newArray};
- }, { new: [] }).new;
- return result;
- }
- function smallest(arr, sortArr, sortLevel) {
- try {
- return arr.reduce((ac, cv, i , arr) => {
- return whichIsSmaller(ac, cv, sortArr, sortLevel);
- })
- } catch(e) { console.error('Error - Trying to sort by an object instead of a value!'); }
- }
- function whichIsSmaller(ac, cv, sortArr, sortLevel) {
- const acVal = sortByRetriever(ac, sortArr[sortLevel].by);
- const curVal = sortByRetriever(cv, sortArr[sortLevel].by);
- if(typeof acVal === 'object' || typeof curVal === 'object') { throw(true); } else
- if(ifMC(acVal) < ifMC(curVal)) { return ac; } else
- if(ifMC(acVal) > ifMC(curVal)) { return cv; } else
- // comparitor and accumulator are same - if last sort level return ac
- if(sortArr.length - 1 === sortLevel) { return ac; }
- else {
- // There are more sort levels - increase the sort level
- sortLevel++;
- if(sortArr[sortLevel].dir && sortArr[sortLevel].dir === 'desc') {
- return whichIsLarger(ac, cv, sortArr, sortLevel);
- } else { return whichIsSmaller(ac, cv, sortArr, sortLevel); }
- }
- }
- function largest(arr, sortArr, sortLevel) {
- try {
- return arr.reduce((ac, cv, i , arr) => {
- return whichIsLarger(ac, cv, sortArr, sortLevel);
- })
- } catch(e) { console.error('Error - Trying to sort by an object instead of a value!'); }
- }
- function whichIsLarger(ac, cv, sortArr, sortLevel) {
- const acVal = sortByRetriever(ac, sortArr[sortLevel].by);
- const curVal = sortByRetriever(cv, sortArr[sortLevel].by);
- if(typeof acVal === 'object' || typeof curVal === 'object') { throw(true); } else
- if(ifMC(acVal) > ifMC(curVal)) { return ac; } else
- if(ifMC(acVal) < ifMC(curVal)) { return cv; } else
- // comparitor and accumulator are same - if last sort level return ac
- if(sortArr.length - 1 === sortLevel) { return ac; }
- else {
- // There are more sort levels - increase the sort level
- sortLevel++;
- if(sortArr[sortLevel].dir && sortArr[sortLevel].dir === 'desc') {
- return whichIsLarger(ac, cv, sortArr, sortLevel);
- } else { return whichIsSmaller(ac, cv, sortArr, sortLevel); }
- }
- }
- function sortByRetriever(obj, by) {
- return by.split('/').reduce((ac, cv) => {
- return ac[cv];
- }, obj);
- }
- function ifMC(arg) {
- // if string make mixed case all lower case
- return typeof arg === 'string' ? arg.toLowerCase() : arg;
- }
Add Comment
Please, Sign In to add comment