Skip to content

Instantly share code, notes, and snippets.

@benlesh
Last active November 3, 2016 20:25
Show Gist options
  • Save benlesh/1300c5039c0999bb8a33e92819b59429 to your computer and use it in GitHub Desktop.
Save benlesh/1300c5039c0999bb8a33e92819b59429 to your computer and use it in GitHub Desktop.
Primitive path evaluation implementation with refs
console.clear();
const path = ["list", 0];
const path0 = ["list", 20, 'name']; // ref ref
const path1 = ["list", [0, 1], "name"];
const path2 = ["list", [0, 1, { from: 3, length: 2 }], "name"];
const path3 = ["list", [0, 1, { from: 3, length: 2 }, { from: 7, to: 9 }], "name"];
const path4 = ["list", { to: 5 }, ["name", "rating"]];
const jsonGraph = {
list: [
{ $type: 'ref', value: ['videos', 123 ] },
{ $type: 'ref', value: ['videos', 124 ] },
{ $type: 'ref', value: ['videos', 125 ] },
{ $type: 'ref', value: ['videos', 126 ] },
{ $type: 'ref', value: ['videos', 127 ] },
{ $type: 'ref', value: ['videos', 123 ] },
{ $type: 'ref', value: ['videos', 124 ] },
{ $type: 'ref', value: ['videos', 125 ] },
{ $type: 'ref', value: ['videos', 126 ] },
{ $type: 'ref', value: ['videos', 127 ] },
{ $type: 'ref', value: ['videos', 123 ] },
{ $type: 'ref', value: ['videos', 124 ] },
{ $type: 'ref', value: ['videos', 125 ] },
{ $type: 'ref', value: ['videos', 126 ] },
{ $type: 'ref', value: ['videos', 127 ] },
{ $type: 'ref', value: ['videos', 123 ] },
{ $type: 'ref', value: ['videos', 124 ] },
{ $type: 'ref', value: ['videos', 125 ] },
{ $type: 'ref', value: ['videos', 126 ] },
{ $type: 'ref', value: ['videos', 127 ] },
{ $type: 'ref', value: ['list', 0 ] }
],
videos: {
'123': {
name: "House of cards",
rating: 5
},
'124': {
name: 'Daredevil',
rating: 5
},
'125': {
name: 'Stranger Things',
rating: 5
},
'126': {
name: 'Three-Headed Shark Attack',
rating: 2.7
},
'127': {
name: 'Sharktopus',
rating: 1.3
}
}
}
function buildJson(path, json, root) {
if (isValueType(json)) {
return json;
}
if (path.length === 0) {
return json;
}
if (json.$type === 'ref') {
return buildJson(path, getPath(json.value, root), root);
}
const [p, ...nextLeft] = path;
if (Array.isArray(p)) {
return p.reduce((s, n, i) => {
s[n] = buildJson([n, ...nextLeft], json, root)[n];
return s;
}, {});
}
if (typeof p === 'object') {
return buildJson([toIndices(p), ...nextLeft], json, root)
}
const v = buildJson(nextLeft, json[p], root);
return {
[p]: v
};
}
function toIndices({ from = 0, to, length }) {
const len = (typeof to === 'undefined') ? length : to - from + 1;
return Array.from({ length: len }, (_, i) => i + from);
}
// console.log(toIndices({ from: 0, to: 5 }));
// console.log(toIndices({ from: 2, to: 5 }));
// console.log(toIndices({ to: 5 }));
// console.log(toIndices({ from: 2, length: 2 }));
// console.log(toIndices({ length: 6 }));
function isValueType(o) {
return typeof o !== 'object' && o !== null;
}
function getPath(path, json) {
if (path.length === 0) {
return json;
}
const [p, ...pathLeft] = path;
return getPath(pathLeft, json[p]);
}
function test(path) {
let result = buildJson(path, jsonGraph, jsonGraph);
console.log(JSON.stringify(path));
console.log(JSON.stringify(result, null, 2))
}
[path, path0, path1, path2, path3, path4].forEach(test)
const jsonGraph = {
list: [
{ $type: 'ref', value: ['videos', 123 ] },
{ $type: 'ref', value: ['videos', 124 ] },
{ $type: 'ref', value: ['videos', 125 ] },
{ $type: 'ref', value: ['videos', 126 ] },
{ $type: 'ref', value: ['videos', 127 ] },
{ $type: 'ref', value: ['videos', 123 ] },
{ $type: 'ref', value: ['videos', 124 ] },
{ $type: 'ref', value: ['videos', 125 ] },
{ $type: 'ref', value: ['videos', 126 ] },
{ $type: 'ref', value: ['videos', 127 ] },
{ $type: 'ref', value: ['videos', 123 ] },
{ $type: 'ref', value: ['videos', 124 ] },
{ $type: 'ref', value: ['videos', 125 ] },
{ $type: 'ref', value: ['videos', 126 ] },
{ $type: 'ref', value: ['videos', 127 ] },
{ $type: 'ref', value: ['videos', 123 ] },
{ $type: 'ref', value: ['videos', 124 ] },
{ $type: 'ref', value: ['videos', 125 ] },
{ $type: 'ref', value: ['videos', 126 ] },
{ $type: 'ref', value: ['videos', 127 ] },
{ $type: 'ref', value: ['list', 0 ] }
],
videos: {
'123': {
name: "House of cards",
rating: 5
},
'124': {
name: 'Daredevil',
rating: 5
},
'125': {
name: 'Stranger Things',
rating: 5
},
'126': {
name: 'Three-Headed Shark Attack',
rating: 2.7
},
'127': {
name: 'Sharktopus',
rating: 1.3
}
}
}
const path = ["list", 0];
const path0 = ["list", 20, 'name']; // ref ref
const path1 = ["list", [0, 1], "name"];
const path2 = ["list", [0, 1, { from: 3, length: 2 }], "name"];
const path3 = ["list", [0, 1, { from: 3, length: 2 }, { from: 7, to: 9 }], "name"];
const path4 = ["list", { to: 5 }, ["name", "rating"]];
// const routes = [
// {
// route: 'list[{integers}]',
// get(actualPathSet) {
// // actualPathSet = ["list", [0, 1]]
// return Promise.resolve(buildRootJson(actualPathSet, jsonGraph));
// },
// },
// {
// route: 'videos[{integers}].name',
// get(actualPathSet) {
// // actualPathSet = ["videos", [123], "name"]
// return Promise.resolve(buildRootJson(actualPathSet, jsonGraph));
// },
// }
// ];
const routeTree = {
list: {
_integers: {
_result: async function (actualPathSet) {
// actualPathSet = ["list", [0, 1]]
return Promise.resolve(
{
list: {
0: { $type: 'ref', value: ['videos', 123] },
1: { $type: 'ref', value: ['videos', 124] }
}
}
);
},
}
},
videos: {
_integers: {
name: {
_result: function (actualPathSet) {
// actualPathSet = ["videos", [123], "name"]
return Promise.resolve(
{
videos: {
123: {
name: 'House of Cards'
}
}
}
);
}
}
}
}
}
// ['list', 0, 'name']
// {
// list: {
// '0': { $type: 'ref', value: ['videos', 123]}
// },
// videos: {
// 123: {
// 'name': 'Whatever'
// }
// }
// }
function lookupTreeNode(tree, key) {
let node = tree[key];
if (parseInt(key) == key) {
node = tree._integers;
}
return node;
}
async function routerGet(rootJsonGraph, currentJsonGraphNode,
rootRouteTree, currentRouteTree,
currentPath, pathSet) {
const [p, ...nextPath] = currentPath;
if (currentJsonGraphNode) {
console.log('has');
// it's a ref
if (currentJsonGraphNode.$type === 'ref') {
return routerGet(rootJsonGraph, rootJsonGraph, rootRouteTree,
rootRouteTree, [...currentJsonGraphNode.value, ...currentPath],
[...currentJsonGraphNode.value, ...currentPath]);
}
let nextGraphNode = currentJsonGraphNode[p];
let nextRouteTree = lookupTreeNode(currentRouteTree, p);
return routerGet(rootJsonGraph, nextGraphNode, rootRouteTree,
nextRouteTree, nextPath, pathSet);
} else if (currentRouteTree) {
console.log('nope');
if (currentRouteTree._result) {
const result = await currentRouteTree._result(pathSet);
// HACK: This should be a recursive mixin!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
Object.assign(rootJsonGraph, result);
return routerGet(rootJsonGraph, rootJsonGraph, rootRouteTree,
rootRouteTree, pathSet, pathSet);
} else {
let nextRouteTree = lookupTreeNode(currentRouteTree, p);
return routerGet(rootJsonGraph, undefined, rootRouteTree,
nextRouteTree, nextPath, pathSet);
}
} else if (currentPath.length === 0) {
console.log('DONE!!!!!');
return rootJsonGraph;
} else {
throw new Error('no route');
}
}
const cache = {};
routerGet(cache, cache, routeTree, routeTree, ['list', 0, 'name'], ['list', 0, 'name'])
.then(x => console.log(JSON.stringify(x, null, 2)));
routerGet(cache, cache, routeTree, routeTree, ['list', 0, 'name'], ['list', 0, 'name'])
.then(x => console.log(JSON.stringify(x, null, 2)));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment