cow-value-model.js 2.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374
  1. function copyWith (obj, key, value) {
  2. const result = typeof key === 'number' ? obj == null ? [] : [...obj] : obj == null ? {} : {...obj};
  3. result[key] = value;
  4. return result;
  5. }
  6. const constructKeys = function (keyDescriptions) {
  7. const keys = [];
  8. function fillKeys (keyDescriptions) {
  9. keyDescriptions.forEach(each => {
  10. if (typeof each === 'number') keys.push(each);
  11. else if (Array.isArray(each)) fillKeys(each);
  12. else keys.push(...each.toString().split('.'));
  13. });
  14. }
  15. fillKeys(keyDescriptions);
  16. return keys;
  17. };
  18. const deepGet = (keys, obj) => keys.reduce((x, key) => x == null ? undefined : x[key], obj);
  19. const deepPut = (keys, obj, val) => {
  20. function setVal (x, index) {
  21. if (index >= keys.length) return val;
  22. const key = keys[index],
  23. value = x == null ? undefined : x[key],
  24. modified = setVal(value, index + 1);
  25. return value === modified ? x : copyWith(x, key, modified);
  26. }
  27. return setVal(obj, 0);
  28. };
  29. export const deepGetOrNil = (...keyDescriptions) => {
  30. const keys = constructKeys(keyDescriptions);
  31. return obj => deepGet(keys, obj);
  32. };
  33. export const deget = deepGetOrNil;
  34. export const deepCopyOnWrite = (...keyDescriptions) => {
  35. const keys = constructKeys(keyDescriptions);
  36. return val => obj => deepPut(keys, obj, val);
  37. };
  38. export const decow = deepCopyOnWrite;
  39. export const cowValueModel = (...keyDescriptions) => {
  40. const keys = constructKeys(keyDescriptions);
  41. const GET_SENTINEL = {};
  42. return (obj, val = GET_SENTINEL) =>
  43. val === GET_SENTINEL ?
  44. deepGet(keys, obj) :
  45. deepPut(keys, obj, val);
  46. };
  47. export const cowWorkshop = (keys, fn = x => x) => (obj, {result = obj, resultKeys = keys, diff} = {}) => {
  48. keys.forEach((key, index) => {
  49. const value = fn(deget(key)(obj));
  50. if (typeof value === "undefined") return;
  51. const modifier = decow(resultKeys[index])(value);
  52. const oldResult = result;
  53. result = modifier(oldResult);
  54. if (result !== oldResult) {
  55. diff = modifier(diff);
  56. }
  57. });
  58. return {result, diff};
  59. };