index.js 2.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. const copyArrayWith = (key, value) => obj => {
  2. const result = obj == null ? [] : [...obj];
  3. result[key] = value;
  4. return result;
  5. };
  6. const copyMapWith = (key, value) => obj => {
  7. const result = obj == null ? new Map() : new Map(obj);
  8. result.set(key, value);
  9. return result;
  10. };
  11. const copyObjectWith = (key, value) => obj => {
  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 = (key, value) =>
  20. typeof key === 'number' ? copyArrayWith(key, value) :
  21. isMapKey(key) ? copyMapWith(key.key, value) :
  22. copyObjectWith(key, value);
  23. function parseKeysInto (keyDescriptions, keys) {
  24. keyDescriptions.forEach(each => {
  25. if (typeof each === 'number' || isMapKey(each)) keys.push(each);
  26. else if (Array.isArray(each)) parseKeysInto(each, keys);
  27. else keys.push(...each.toString().split('.'));
  28. });
  29. return keys;
  30. }
  31. const getKey = (x, key) => x == null ? undefined : isMapKey(key) ? x.get(key.key) : x[key];
  32. const getKeyWithCheck = (x, key) =>
  33. x == null ? null :
  34. isMapKey(key) ? x.has(key.key) ? {value: x.get(key.key)} : null :
  35. x.hasOwnProperty(key) ? {value: x[key]} : null;
  36. const chain = list => step => stop => (function worker (index) {
  37. return index >= list.length ? stop : step(list[index], worker(index + 1));
  38. })(0);
  39. const id = x => x;
  40. const modifier = (key, value, valueModifier) => {
  41. const modified = valueModifier(value);
  42. return value === modified ? id : copyWith(key, modified);
  43. };
  44. const atPathBeginningWith = prefix => (...keyDescriptions) => {
  45. const keys = parseKeysInto(keyDescriptions, [...prefix]),
  46. atCurrentPath = atPathBeginningWith(keys),
  47. keyChain = chain(keys),
  48. putChain = keyChain((key, next) => x => modifier(key, getKey(x, key), next)(x)),
  49. mapChain = keyChain((key, next) => x => {
  50. const valueWithCheck = getKeyWithCheck(x, key);
  51. if (valueWithCheck == null) return x;
  52. return modifier(key, valueWithCheck.value, next)(x);
  53. });
  54. return Object.assign(atCurrentPath, {
  55. get: obj => keys.reduce(getKey, obj),
  56. put: val => putChain(() => val),
  57. map: mapChain
  58. });
  59. };
  60. export const atPath = atPathBeginningWith([]);
  61. export const pathWorkshop = (keys, fn = x => x) => (obj, {result = obj, resultKeys = keys, diff} = {}) => {
  62. keys.forEach((key, index) => {
  63. const value = fn(atPath(key).get(obj));
  64. if (typeof value === "undefined") return;
  65. const modifier = atPath(resultKeys[index]).put(value);
  66. const oldResult = result;
  67. result = modifier(oldResult);
  68. if (result !== oldResult) {
  69. diff = modifier(diff);
  70. }
  71. });
  72. return {result, diff};
  73. };