Guest User

Untitled

a guest
May 23rd, 2018
83
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 8.79 KB | None | 0 0
  1. /*
  2. * Store is a JSON object that holds no references (pure data only).
  3. * Nodes can be locked and unlocked to aid in data not being accidentally changed.
  4. * Nodes can be added and deleted and any parent nodes are added / cleaned up automatically.
  5. *
  6. * create store...
  7. * let store = storeCreator({});
  8. *
  9. *
  10. * store.set('lvl1/lvl2', 'hello') // Updates the node and adds any parents if missing
  11. * store.set('lvl1/lvl2', undefined) // Deletes the node and any parent nodes without any children (undefined / null / [] / {}) all result in delete
  12. * store.get(); // gets the full contents of store
  13. * store.get('lvl1'); // gets the contents of store at a particular node
  14. * store.lock(); // Restricts any changes to the store
  15. * store.unlock(); // Removes the lock
  16. *
  17. */
  18.  
  19. // TODO: FINISH UPDATE
  20.  
  21.  
  22. let state = storeCreator({});
  23. state.push('l1',[
  24. {cat: 'red', val: 'a'},
  25. {cat: 'blue', val: 'b'},
  26. {cat: 'red', val: 'c'},
  27. {cat: 'red', val: 'd'},
  28. {cat: 'red', val: 0}
  29. ]);
  30.  
  31. state.unshift('l1',[{cat: 'red', val: 0}, {cat: 'red', val: 0}]);
  32.  
  33. state.apply('l1', pushArray => pushArray.pop());
  34.  
  35.  
  36. let result = JSON.stringify(state.get('l1', {
  37. limit: 6, meta: true
  38. }), null, 2);
  39.  
  40. // print result
  41. document.querySelector('#result')
  42. .innerHTML = result;
  43.  
  44.  
  45. function storeCreator(obj) {
  46. let store = cloner(obj);
  47. let locked = false;
  48. let idCount = 1;
  49.  
  50. return {
  51. get: (key, options) => get(key, options, store),
  52. set: (key, val) => {
  53. if (locked) {
  54. console.log('This Store is locked. Unlock to perform set operations');
  55. } else {
  56. store = iu(store, key, val);
  57. }
  58. },
  59. push: (key, val) => {
  60. if (locked) {
  61. console.log('This Store is locked. Unlock to perform push operations');
  62. } else {
  63. const valArray = isArray(val)? val : [val];
  64. const rows = valArray.reduce((ac,cv) => {
  65. ac.push({id: idCount, value: cv});
  66. idCount++;
  67. return ac;
  68. }, get(key, undefined, store) || []);
  69. store = iu(store, key, rows);
  70. }
  71. },
  72. unshift: (key, val) => {
  73. if (locked) {
  74. console.log('This Store is locked. Unlock to perform push operations');
  75. } else {
  76. const valArray = isArray(val)? val : [val];
  77. const rows = valArray.reduce((ac,cv) => {
  78. ac.unshift({id: idCount, value: cv});
  79. idCount++;
  80. return ac;
  81. }, get(key, undefined, store) || []);
  82. store = iu(store, key, rows);
  83. }
  84. },
  85. apply(key, func) {
  86. const pushArray = get(key, undefined, store);
  87. func(pushArray);
  88. },
  89. lock: () => {
  90. locked = true;
  91. },
  92. unlock: () => {
  93. locked = false;
  94. }
  95. };
  96. }
  97.  
  98. function isArray(val) {
  99. return typeof val === 'object' && Array.isArray(val);
  100. }
  101.  
  102. function get (key, options, store) {
  103. if (!key && !options) { return store; }
  104. if (!key && options) { return queryResult(store, options); }
  105. else if (key) {
  106. const subArray = keyToArray(key, store);
  107. const result = subArray.reduce((ac, cv, i, arr) => {
  108. return ac[cv];
  109. }, store);
  110. if (!options) { return result } else {
  111. return queryResult(result, options);
  112. }
  113. }
  114. }
  115.  
  116. function queryResult(obj, options) {
  117. /*
  118. * queries are expected to be performed on Map objects - not arrays... see readme
  119. *
  120. * options
  121. * - filter: fn => boolean: Function that returns boolean for each result
  122. * - limit: number: Limits the final result set to the specified limit
  123. * - sort: Array<[string, asc | desc]>: Sorts the result set by this key
  124. */
  125. obj = filterResults(obj, options.filter);
  126. obj = sortResults(obj, options.sort);
  127. obj = limitResults(obj, options.limit);
  128. if (!options.meta) {
  129. return Object.keys(obj).map(key => obj[key].value);
  130. } else { return obj; }
  131. }
  132.  
  133. function filterResults(obj, filter) {
  134. if(!filter) { return Object.keys(obj).map(key => obj[key]); }
  135. else {
  136. return Object
  137. .keys(obj) // get keys as array
  138. .map(key => obj[key]) // get the values as array
  139. .filter(item => filter(item)) // apply the filter function to each item in array
  140. }
  141. }
  142.  
  143. function sortResults(obj, sort) {
  144. if (!sort) { return obj } else {
  145. const sortObjArray = isArray(sort) ? sort : [sort];
  146. const values = numberSort(objToValueArray(obj), sortObjArray);
  147. return values;
  148. }
  149. }
  150.  
  151. function objToValueArray(obj) {
  152. return Object.keys(obj).map(key => obj[key]);
  153. }
  154.  
  155. function limitResults(obj, limit) {
  156. if (!limit) { return obj } else {
  157. const newKeys = Object.keys(obj);
  158. if (limit < newKeys.length ) { newKeys.length = limit }
  159. return newKeys.map(key => obj[key]);
  160. }
  161. }
  162.  
  163. function iu(state, subArray, val) {
  164. subArray = keyToArray(subArray, state);
  165. return reducer(state, subArray, cloner(val), 0);
  166. }
  167.  
  168. function keyToArray(subArray, state) {
  169. try {
  170. if(!subArray) { throw 'No target provided'; } else
  171. if(subArray.length < 1) { throw 'No target provided'; } else
  172. if(!state) { throw 'No state object provided'; }
  173. return subArray.split('/').filter(segment => segment);
  174. } catch(error) { console.error('Error in reading keys', error)}
  175. }
  176.  
  177. function reducer(_state, subArray, val, l) {
  178. try {
  179. const key = subArray[l];
  180. if(l+1 === subArray.length) {
  181. const value = val;
  182. const replacer =
  183. val === undefined ||
  184. val === null ||
  185. typeof val === 'object' && Array.isArray(val) && val.length === 0 ||
  186. typeof val === 'object' && !Array.isArray(val) && Object.keys(val).length === 0 ? undefined : val;
  187. return Object.assign({}, _state, { [key]: replacer });
  188. }
  189. else {
  190. const value = _state[key] ? _state[key] : {};
  191. return Object.assign({}, _state, {[key]: reducer(value, subArray, val, l+1)} );
  192. }
  193. } catch(error) { console.error('reducer error =>', error);}
  194. }
  195.  
  196. function cloner(arg) {
  197. return JSON.parse(JSON.stringify(arg));
  198. }
  199.  
  200. function checkLocks(locks, keyToFind) {
  201. const lockKeys = Object.keys(locks);
  202. if(lockKeys.some(lockKey => keyToFind.match(lockKey))) {
  203. throw 'This node or parent node is locked'
  204. }
  205. }
  206.  
  207. // array sort functions
  208.  
  209. function numberSort(arr, sortArr, direction) {
  210. let sortLevel = 0;
  211. const result = arr.reduce((ac, cv, i, arr) => {
  212. if(!ac.old) { ac.old = arr }
  213. const targetItem = sortArr[sortLevel].dir && sortArr[sortLevel].dir === 'desc' ?
  214. largest(ac.old, sortArr, sortLevel)
  215. :
  216. smallest(ac.old, sortArr, sortLevel);
  217. const newArray = [...ac.new, targetItem];
  218. const oldArray = ac.old.filter(item => item !== targetItem);
  219. return { old: oldArray, new: newArray};
  220. }, { new: [] }).new;
  221. return result;
  222. }
  223.  
  224. function smallest(arr, sortArr, sortLevel) {
  225. try {
  226. return arr.reduce((ac, cv, i , arr) => {
  227. return whichIsSmaller(ac, cv, sortArr, sortLevel);
  228. })
  229. } catch(e) { console.error('Error - Trying to sort by an object instead of a value!'); }
  230. }
  231.  
  232. function whichIsSmaller(ac, cv, sortArr, sortLevel) {
  233. const acVal = sortByRetriever(ac, sortArr[sortLevel].by);
  234. const curVal = sortByRetriever(cv, sortArr[sortLevel].by);
  235. if(typeof acVal === 'object' || typeof curVal === 'object') { throw(true); } else
  236. if(ifMC(acVal) < ifMC(curVal)) { return ac; } else
  237. if(ifMC(acVal) > ifMC(curVal)) { return cv; } else
  238. // comparitor and accumulator are same - if last sort level return ac
  239. if(sortArr.length - 1 === sortLevel) { return ac; }
  240. else {
  241. // There are more sort levels - increase the sort level
  242. sortLevel++;
  243. if(sortArr[sortLevel].dir && sortArr[sortLevel].dir === 'desc') {
  244. return whichIsLarger(ac, cv, sortArr, sortLevel);
  245. } else { return whichIsSmaller(ac, cv, sortArr, sortLevel); }
  246. }
  247. }
  248.  
  249. function largest(arr, sortArr, sortLevel) {
  250. try {
  251. return arr.reduce((ac, cv, i , arr) => {
  252. return whichIsLarger(ac, cv, sortArr, sortLevel);
  253. })
  254. } catch(e) { console.error('Error - Trying to sort by an object instead of a value!'); }
  255. }
  256.  
  257. function whichIsLarger(ac, cv, sortArr, sortLevel) {
  258. const acVal = sortByRetriever(ac, sortArr[sortLevel].by);
  259. const curVal = sortByRetriever(cv, sortArr[sortLevel].by);
  260. if(typeof acVal === 'object' || typeof curVal === 'object') { throw(true); } else
  261. if(ifMC(acVal) > ifMC(curVal)) { return ac; } else
  262. if(ifMC(acVal) < ifMC(curVal)) { return cv; } else
  263. // comparitor and accumulator are same - if last sort level return ac
  264. if(sortArr.length - 1 === sortLevel) { return ac; }
  265. else {
  266. // There are more sort levels - increase the sort level
  267. sortLevel++;
  268. if(sortArr[sortLevel].dir && sortArr[sortLevel].dir === 'desc') {
  269. return whichIsLarger(ac, cv, sortArr, sortLevel);
  270. } else { return whichIsSmaller(ac, cv, sortArr, sortLevel); }
  271. }
  272. }
  273.  
  274. function sortByRetriever(obj, by) {
  275. return by.split('/').reduce((ac, cv) => {
  276. return ac[cv];
  277. }, obj);
  278. }
  279.  
  280. function ifMC(arg) {
  281. // if string make mixed case all lower case
  282. return typeof arg === 'string' ? arg.toLowerCase() : arg;
  283. }
Add Comment
Please, Sign In to add comment