Skip to content

Instantly share code, notes, and snippets.

@gabejohnson
Last active August 9, 2018 18:46
Show Gist options
  • Save gabejohnson/89817a96346a5e770ec17c7415620d66 to your computer and use it in GitHub Desktop.
Save gabejohnson/89817a96346a5e770ec17c7415620d66 to your computer and use it in GitHub Desktop.
Simple DSL for accessing/updating deeply nested data
/*
Calling _.foo.bar with a value returns the value at the end of that path (if it exists), `undefined` otherwise.
Constructing _.foo.bar with a function returns a function that immutably updates a value if the path matches, returns the original otherwise.
Usage:
> [{foo: {bar: 1}}, {foo: {bar: 2}}, {foo: {bar: 3}}, {foo: {bur: 4}}].map(_.foo.bar)
[1, 2, 3, undefined]
> [{foo: {bar: 1}}, {foo: {bar: 2}}, {foo: {bar: 3}}, {foo: {bur: 4}}].map(new _.foo.bar(x => x + 1))
[{foo: {bar: 2}}, {foo: {bar: 3}}, {foo: {bar: 4}}, {foo: {bur: 4}}]
*/
const _ = new Proxy(Object.create(null), {
get: (obj, prop) => {
const paths = [prop];
const getter = new Proxy(function(){}, {
get: (obj, prop) => {
paths.push(prop);
return getter;
},
apply: (target, thisArg, [result]) => {
for (let i = 0; i < paths.length; i++) {
if (result.hasOwnProperty(paths[i])) result = result[paths[i]];
else return;
}
return result;
},
construct: (target, [fn], newTarget) => result => {
const ogObj = result;
let newObj = objRef = {...result}, i;
for (i = 0; i < paths.length; i++) {
if (result.hasOwnProperty(paths[i])) {
result = result[paths[i]];
if (i < paths.length - 1 && Object.getOwnPropertyNames(result).length > 0) {
objRef = objRef[paths[i]] = {...result};
}
} else {
return ogObj;
}
}
objRef[paths[i-1]] = fn(result);
return newObj;
}
});
return getter;
}
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment