ARTICLE AD BOX
Meanwhile found a solution which is very close to the database functions I have mentioned.
function connectBy(array, startWith, connectWith, path = [], level = 0) { const filterFn = typeof startWith == 'function' ? startWith : function (x) { const children = startWith[startWith.length - 1][connectWith[1]]; return Array.isArray(children) ? children.includes(x[connectWith[0]]) : children == x[connectWith[0]]; }; for (let item of array.filter(filterFn)) { if (path.includes(array.indexOf(item))) { item.isCycle = true; continue; // -> Avoid infinite recursion } item.level = level; item.path = path.concat(array.indexOf(item)); typeof startWith == 'function' ? startWith = [item] : startWith.push(item); startWith = connectBy(array, startWith, connectWith, item.path, level + 1); } return startWith; }The startWith parameter is a function to get the top-level items. connectWith is an array of two strings to define the fields for joining.
With added path and level information it is very simple to generate the output or make other transformations.
arr = connectBy(myArray, function (val) { return val.name == 'Alice'}, ['name', 'children']); console.log(arr.map(x => ' '.repeat(2 * x.level) + x.name).join('\n')) Alice Bob Cindy David Don Bill console.log(arr.map(x => x.path.map(m => myArray[m].name).join(' -> ')).join('\n')) Alice Alice -> Bob Alice -> Bob -> Cindy Alice -> Bob -> Cindy -> David Alice -> Bob -> Cindy -> Don Alice -> BillIt also works the other way around
myArray = [ { name: 'Alice' }, { name: 'Bob', parent: 'Alice' }, { name: 'Bill', parent: 'Alice' }, { name: 'Cindy', parent: 'Bob' }, { name: 'David', parent: 'Cindy' }, { name: 'Don', parent: 'Cindy' } ] arr = connectBy(myArray, (val) => { return val.parent == null }, ['parent', 'name']);Or with One-to-Many relations:
myArray = [ { name: 'Adam', children: ['Britney'] }, { name: 'Alice', children: ['Britney'] }, { name: 'Britney' } ] arr = connectBy(myArray, (x) => { return x.children != null }, ['name', 'children']); console.log(arr.map(x => ' '.repeat(2 * x.level) + x.name).join('\n')) Adam Britney Alice Britney