Created
March 7, 2018 22:17
-
-
Save stephanbogner/4b590f992ead470658a5ebf09167b03d to your computer and use it in GitHub Desktop.
Create tree structure from paths array
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var paths = [ | |
["Account"], | |
["Account", "Payment Methods"], | |
["Account", "Payment Methods", "Credit Card"], | |
["Account", "Payment Methods", "Paypal"], | |
["Account", "Emails"], | |
["Account", "Emails", "Main Email"], | |
["Account", "Emails", "Backup Email"], | |
["Account", "Devices"], | |
["Account", "Devices", "Google Pixel"], | |
["Account", "Devices", "iPad Mini"], | |
["Account", "Devices", "Laptop"] | |
]; | |
var tree = arrangeIntoTree(paths); | |
console.log(JSON.stringify(tree, null, 4)); | |
// Result | |
// [ | |
// { | |
// "name": "Account", | |
// "children": [ | |
// { | |
// "name": "Payment Methods", | |
// "children": [ | |
// { | |
// "name": "Credit Card", | |
// "children": [] | |
// }, | |
// { | |
// "name": "Paypal", | |
// "children": [] | |
// } | |
// ] | |
// }, | |
// { | |
// "name": "Emails", | |
// "children": [ | |
// { | |
// "name": "Main Email", | |
// "children": [] | |
// }, | |
// { | |
// "name": "Backup Email", | |
// "children": [] | |
// } | |
// ] | |
// }, | |
// { | |
// "name": "Devices", | |
// "children": [ | |
// { | |
// "name": "Google Pixel", | |
// "children": [] | |
// }, | |
// { | |
// "name": "iPad Mini", | |
// "children": [] | |
// }, | |
// { | |
// "name": "Laptop", | |
// "children": [] | |
// } | |
// ] | |
// } | |
// ] | |
// } | |
// ] | |
function arrangeIntoTree(paths) { | |
// Adapted from http://brandonclapp.com/arranging-an-array-of-flat-paths-into-a-json-tree-like-structure/ | |
var tree = []; | |
for (var i = 0; i < paths.length; i++) { | |
var path = paths[i]; | |
var currentLevel = tree; | |
for (var j = 0; j < path.length; j++) { | |
var part = path[j]; | |
var existingPath = findWhere(currentLevel, 'name', part); | |
if (existingPath) { | |
currentLevel = existingPath.children; | |
} else { | |
var newPart = { | |
name: part, | |
children: [], | |
} | |
currentLevel.push(newPart); | |
currentLevel = newPart.children; | |
} | |
} | |
} | |
return tree; | |
function findWhere(array, key, value) { | |
// Adapted from https://stackoverflow.com/questions/32932994/findwhere-from-underscorejs-to-jquery | |
t = 0; // t is used as a counter | |
while (t < array.length && array[t][key] !== value) { t++; }; // find the index where the id is the as the aValue | |
if (t < array.length) { | |
return array[t] | |
} else { | |
return false; | |
} | |
} | |
} |
might be missing something but does line 90 need to be there? seems to be working without it?
Honestly ... I don't know ... it's been so many years since I wrote the code ^^
haha yes 5 years, very fair - this was helpful non the less thank you!
Thanks! I ended up writing a more modern typescript version of it.
Its input-output is a bit different as it works with data objects instead of plain paths.
This allows to have data attached / retained to every path node.
export interface TreeNode<TData=any> {
name:string,
children?:TreeNode[],
data?:TData,
}
export function arrangeObjectsIntoTree<TData extends Record<string,any>>(objects:TData[], pathProperty:string):TreeNode<TData>[] {
const tree = [];
for (let i = 0; i < objects.length; i++) {
const obj = objects[i];
let path:string[];
if(Array.isArray(obj[pathProperty])) path = obj[pathProperty] ;
else if (typeof obj[pathProperty] === 'string') path = obj[pathProperty].split('/');
else throw new Error('Could not parse path property')
let currentParentNodeList:TreeNode<TData>[] = tree;
for (let j = 0; j < path.length; j++) {
const pathSegment = path[j];
const isFinalSegment = j+1 === path.length;
const existingNode = currentParentNodeList.find(node=>node.name === pathSegment)
if (existingNode) {
currentParentNodeList = existingNode.children;
} else if(isFinalSegment) {
currentParentNodeList.push({
name: pathSegment,
data: obj,
});
} else {
currentParentNodeList.push({
name: pathSegment,
children: [],
});
currentParentNodeList = currentParentNodeList[currentParentNodeList.length-1].children;
}
}
}
return tree;
}
Usage example:
arrangeObjectsIntoTree([
{ foo:'bar', path:'my/tree/dir' },
{ foo:'bar', path:['my','other','dir'] },
], 'path'
Just like the original script, it will create a nested structure of objects containing name and children properties.
Additionally, each "entry" node will contain a data property which stores the original object.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@nickgrato
Honestly ... I don't know ... it's been so many years since I wrote the code ^^