cow-value-model.js 2.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. function copyArrayWith (obj, key, value) {
  2. const result = obj == null ? [] : [...obj];
  3. result[key] = value;
  4. return result;
  5. }
  6. function copyMapWith (obj, key, value) {
  7. const result = obj == null ? new Map() : new Map(obj);
  8. result.set(key, value);
  9. return result;
  10. }
  11. function copyObjectWith (obj, key, value) {
  12. const result = obj == null ? {} : {...obj};
  13. result[key] = value;
  14. return result;
  15. }
  16. const isMapKey = key => typeof key === 'object' && key.isKeyInMap === true;
  17. export const keyInMap = key => ({isKeyInMap: true, key});
  18. export const kim = keyInMap;
  19. const copyWith = (obj, key, value) =>
  20. typeof key === 'number' ? copyArrayWith(obj, key, value) :
  21. isMapKey(key) ? copyMapWith(obj, key.key, value) :
  22. copyObjectWith(obj, key, value);
  23. const constructKeys = function (keyDescriptions) {
  24. const keys = [];
  25. function fillKeys (keyDescriptions) {
  26. keyDescriptions.forEach(each => {
  27. if (typeof each === 'number' || isMapKey(each)) keys.push(each);
  28. else if (Array.isArray(each)) fillKeys(each);
  29. else keys.push(...each.toString().split('.'));
  30. });
  31. }
  32. fillKeys(keyDescriptions);
  33. return keys;
  34. };
  35. const getKey = (x, key) => x == null ? undefined : isMapKey(key) ? x.get(key.key) : x[key];
  36. const deepGet = (keys, obj) => keys.reduce(getKey, obj);
  37. const deepPut = (keys, obj, val) => {
  38. function setVal (x, index) {
  39. if (index >= keys.length) return val;
  40. const key = keys[index],
  41. value = getKey(x, key),
  42. modified = setVal(value, index + 1);
  43. return value === modified ? x : copyWith(x, key, modified);
  44. }
  45. return setVal(obj, 0);
  46. };
  47. export const deepGetOrNil = (...keyDescriptions) => {
  48. const keys = constructKeys(keyDescriptions);
  49. return obj => deepGet(keys, obj);
  50. };
  51. export const deget = deepGetOrNil;
  52. export const deepCopyOnWrite = (...keyDescriptions) => {
  53. const keys = constructKeys(keyDescriptions);
  54. return val => obj => deepPut(keys, obj, val);
  55. };
  56. export const decow = deepCopyOnWrite;
  57. export const cowValueModel = (...keyDescriptions) => {
  58. const GET_SENTINEL = {};
  59. return (obj, val = GET_SENTINEL) =>
  60. (val === GET_SENTINEL ?
  61. deget(...keyDescriptions) :
  62. decow(...keyDescriptions)(val))
  63. (obj);
  64. };
  65. export const cowWorkshop = (keys, fn = x => x) => (obj, {result = obj, resultKeys = keys, diff} = {}) => {
  66. keys.forEach((key, index) => {
  67. const value = fn(deget(key)(obj));
  68. if (typeof value === "undefined") return;
  69. const modifier = decow(resultKeys[index])(value);
  70. const oldResult = result;
  71. result = modifier(oldResult);
  72. if (result !== oldResult) {
  73. diff = modifier(diff);
  74. }
  75. });
  76. return {result, diff};
  77. };