I am working with legacy data in JSON format. The tree represents where boxes are shipped between the product manufacturer, shipper, redistributor, and finally the destination. Right now the JSON tree structure represents the manufacturer to the shipper to - redistributer in a hierarchical manner. However, the final node of the tree, the destination, is part of the redistributer node. This would be easy as I would just have to walk the tree and add an extra node representing the destination. But this structure has nodes that are identical repeated because they represent the destination too.
So, my goal is to merge nodes with identical ids and to add the final destination nodes onto the tree.
The algorithm I have handles the first level of the Redistributers where the children are null. However, when a Redistributor node has children my algorithm fails.
Here is the JSON data and code. I am using javascript too. A lot of the nodes are updated by reference. The big PROBLEM I am having with my work is the any leaf nodes that have children are ignored. The algorithm does not recur over children in the leafs nodes.
This is a really odd case of merging and adding nodes to a tree structure. It's been a brain twister for me. Any help is greatly appreciated. THANK YOU!!
Here is the stackblitz: https://stackblitz.com/edit/js-f3uhaw?file=index.js
var nodes = [
{
type: 'Manufacturer',
loc_id: '1d7d43bb',
loc_name: 'San Diego',
destination_loc_id: '1d7d43ba',
destination_loc_name: 'Palm Springs',
children: [
{
type: 'Shipper',
loc_id: '1d7d43ba',
loc_name: 'Palm Springs',
destination_loc_id: '755eb7ca',
destination_loc_name: 'Los Angeles',
destination_loc_type: 'Redistributer',
children: [
{
type: 'Redistributer',
loc_id: '755eb7ca',
loc_name: 'Los Angeles',
destination_loc_id: '50f51eb6',
destination_loc_name: 'Bakersfield',
destination_loc_type: 'Distributer',
children: null,
},
{
type: 'Redistributer',
loc_id: '755eb7ca',
loc_name: 'Los Angeles',
destination_loc_id: '755eb7ce',
destination_loc_name: 'Santa Monica',
destination_loc_type: 'Distributer',
children: null,
},
{
type: 'Redistributer',
loc_id: '755eb7ca',
loc_name: 'Los Angeles',
destination_loc_id: '797a3711',
destination_loc_name: 'San Pedro',
loc_type: 'Redistributer',
destination_loc_type: 'Distributer',
children: null,
},
{
type: 'Redistributer',
loc_id: '755eb7ca',
loc_name: 'Los Angeles',
destination_loc_id: 'aecf4289',
destination_loc_name: 'Malibu',
loc_type: 'Redistributer',
destination_loc_type: 'Distributer',
children: null,
},
{
type: 'Redistributer',
loc_id: '755eb7ca',
loc_name: 'Los Angeles',
destination_loc_id: 'aecf4288',
destination_loc_name: 'Long Beach',
loc_type: 'Redistributer',
destination_loc_type: 'Distributer',
children: [
{
type: 'Redistributer',
loc_id: 'aecf4288',
loc_name: 'Long Beach',
destination_loc_id: 'aecf4228',
destination_loc_name: 'Santa Ana',
loc_type: 'Redistributer',
destination_loc_type: 'Distributer',
children: null,
},
],
},
],
},
],
},
];
function preProcessNodes() {
for (var i = 0; i < nodes.length; i++) {
let children = nodes[i].children[0].children;
nodes[i].children[0].children = walkTree(children);
console.log(JSON.stringify(nodes));
}
}
function walkTree(children) {
//merge first child nodes here
children = children.filter((x, i) => x.id === children[i].id);
while (children.length > 1) {
const parent = children[0];
const newNode = {
type: parent.type,
loc_id: parent.loc_id,
loc_name: parent.loc_name,
loc_type: parent.loc_type,
children: [],
};
const childs = [];
for (var j = 0; j < children?.length; j++) {
const child = children[j];
childs.push({
loc_id: child.destination_loc_id,
loc_name: child.destination_loc_name,
type: child.destination_loc_type,
children: null,
});
}
newNode.children = childs;
children
.filter((x, i) => x.id === children[i].id)
.forEach((x) => children.splice(children.indexOf(x), 1));
children.push(newNode);
console.log(children);
walkTree(children);
}
return children;
}
preProcessNodes();
The end result I am trying to output is:
[{
type: 'Manufacturer',
loc_id: '1d7d43bb',
loc_name: 'San Diego',
destination_loc_id: '1d7d43ba',
destination_loc_name: 'Palm Springs',
children: [{
type: 'Shipper',
loc_id: '1d7d43ba',
loc_name: 'Palm Springs',
destination_loc_id: '755eb7ca',
destination_loc_name: 'Los Angeles',
destination_loc_type: 'Redistributer',
children: [{
type: 'Redistributer',
loc_id: '755eb7ca',
loc_name: 'Los Angeles',
children: [{
loc_id: '50f51eb6',
loc_name: 'Bakersfield',
type: 'Distributer',
children: null
},
{
loc_id: '755eb7ce',
loc_name: 'Santa Monica',
type: 'Distributer',
children: null
},
{
loc_id: '797a3711',
loc_name: 'San Pedro',
type: 'Distributer',
children: null,
},
{
loc_id: 'aecf4289',
loc_name: 'Malibu',
loc_type: 'Redistributer',
type: 'Distributer',
children: null
},
{
loc_id: 'aecf4282',
loc_name: 'Long Beach',
loc_type: 'Redistributer',
children: [{
loc_id: 'aecf4283',
loc_name: 'Santa Ana',
loc_type: 'Distributer',
children: null,
}]
}
]
}
]
}]
}]
This approach detects duplicate entries (same loc_id) and deletes them. At the same time as the deletion, a child object for the destination_* fields is added to the correct parent's children array.
const nodes = [{"type":"Manufacturer","loc_id":"1d7d43bb","loc_name":"San Diego","destination_loc_id":"1d7d43ba","destination_loc_name":"Palm Springs","children":[{"type":"Shipper","loc_id":"1d7d43ba","loc_name":"Palm Springs","destination_loc_id":"755eb7ca","destination_loc_name":"Los Angeles","destination_loc_type":"Redistributer","children":[{"type":"Redistributer","loc_id":"755eb7ca","loc_name":"Los Angeles","destination_loc_id":"50f51eb6","destination_loc_name":"Bakersfield","destination_loc_type":"Distributer","children":null},{"type":"Redistributer","loc_id":"755eb7ca","loc_name":"Los Angeles","destination_loc_id":"755eb7caX","destination_loc_name":"Santa Monica","destination_loc_type":"Distributer","children":null},{"type":"Redistributer","loc_id":"755eb7ca","loc_name":"Los Angeles","destination_loc_id":"797a3711","destination_loc_name":"San Pedro","loc_type":"Redistributer","destination_loc_type":"Distributer","children":null},{"type":"Redistributer","loc_id":"755eb7ca","loc_name":"Los Angeles","destination_loc_id":"aecf4289","destination_loc_name":"Malibu","loc_type":"Redistributer","destination_loc_type":"Distributer","children":null},{"type":"Redistributer","loc_id":"755eb7ca","loc_name":"Los Angeles","destination_loc_id":"aecf4288","destination_loc_name":"Long Beach","loc_type":"Redistributer","destination_loc_type":"Distributer","children":[{"type":"Redistributer","loc_id":"aecf4288","loc_name":"Long Beach","destination_loc_id":"aecf4228","destination_loc_name":"Santa Ana","loc_type":"Redistributer","destination_loc_type":"Distributer","children":null}]}]}]}]
const lookup = {}
const f = arr => {
let dropList = [];
arr.forEach((i,index)=>{
let existing = lookup[i.loc_id];
if(!existing) {
lookup[i.loc_id] = i;
}
else {
if(existing.destination_loc_id) {
let child = {
loc_id: existing.destination_loc_id,
loc_name: existing.destination_loc_name,
loc_type: existing.destination_loc_type,
children: null
}
delete existing.destination_loc_id;
delete existing.destination_loc_name;
delete existing.destination_loc_type;
(existing.children??=[]).push(child);
lookup[child.loc_id] = child;
}
dropList.push(index);
let child = {
loc_id: i.destination_loc_id,
loc_name: i.destination_loc_name,
loc_type: i.destination_loc_type,
children: null
};
(existing.children??=[]).push(child);
lookup[child.loc_id] = child;
}
if(i.children) f(i.children)
})
dropList.reverse().forEach(index=>arr.splice(index, 1));
}
f(nodes)
console.log(nodes)
Related
I have an array of objects, each containing a path property which holds the value of "paths" to which I'd like to map the array elements to.
let myData = [
{
path: ['Movies', 'Comedies', 'TopRanked'],
name: 'The Hangover',
id: '1',
},
{
path: ['Movies', 'Comedies', 'TopRanked'],
name: 'Eurotrip',
id: '2',
},
{
path: ['Movies', 'Action'],
name: 'Need for Speed',
id: '3',
},
{
path: ['Life'],
name: 'Not so bad',
id: '4',
},
{
path: ['Life', 'Financial', 'Income'],
name: 'Making Hundreds',
id: '5',
},
{
path: ['Life', 'Financial', 'Income'],
name: 'Making Thousands',
id: '6',
},
{
path: ['Life', 'MonthlySpent'],
name: 'Just a little bit',
id: '7',
},
{
path: ['Life', 'MonthlySpent'],
name: 'Living large',
id: '8',
},
];
console.log(myData);
Essentially, the result I am looking for is a breakdown of that array into as many as nested arrays as needed (relative to all possible available paths), with each retaining its "type" - either a parent or an item. So the desired output is like so:
let myTree = [
{
name: 'Movies',
type: 'parent',
children: [
{
name: 'Comedies',
type: 'parent',
children: [
{
name: 'TopRanked',
type: 'parent',
children: [
{
name: 'The Hangover',
type: 'item',
id: 1,
path: ['Movies', 'Comedies', 'TopRanked']
},
{
name: 'Eurotrip',
type: 'item',
id: 2,
path: ['Movies', 'Comedies', 'TopRanked'],
}
]
},
]
},
{
name: 'Action',
type: 'parent',
children: [
{
name: 'Need for Speed',
type: 'item',
id: 3,
path: ['Movies', 'Action'],
},
]
}
]
},
{
name: 'Life',
type: 'parent',
children: [
{
name: 'Not so bad',
type: 'item',
id: 4,
path: ['Life'],
},
{
name: 'Financial',
type: 'parent',
children: [
{
name: 'Income',
type: 'parent',
children: [
{
name: 'Making Hundreds',
type: 'item',
id: 5,
path: ['Life', 'Financial', 'Income'],
},
{
name: 'Making Thousands',
type: 'item',
id: 6,
path: ['Life', 'Financial', 'Income'],
}
]
}
]
},
{
name: 'MonthlySpent',
type: 'parent',
children: [
{
name: 'Just a little bit',
type: 'item',
id: 7,
path: ['Life', 'MonthlySpent'],
},
{
name: 'Living Large',
type: 'item',
id: 8,
path: ['Life', 'MonthlySpent'],
}
]
}
]
}
]
console.log(myTree);
I tried the following, and while the tree structure is created, the "item"-types are not placed as the array-value of the last nested "parent" type:
function treeData(data) {
var result = [],
hash = { _: { children: result } };
data.forEach(function (object) {
object.path.reduce(function (o, p) {
if (!o[p]) {
o[p] = { _: { name: p, children: [] } };
o._.children.push(o[p]._);
}
return o[p];
}, hash)._.name = object.name;
});
return result;
}
Would appreciate a working solution, as I am wracking my head and can't find one. Tnnx.
The approach below follows a similar pattern to your code i.e. loop every object, but instead of a reduce simply loops every item in path and creates a branch off the root. When there are no more 'branches' then add the original object. See the comments.
let myData = data();
let myTree = treeData(data);
console.log(myTree);
function treeData(data) {
let root = {"children": []} // create origin
for (obj of myData) { // loop items in the data
obj.type = "Item"; // add a property to suit your output
let tree = root; // start at root every object
for (path of obj.path) { // loop over items in path
let branch = tree.children.find(k => k.name == path); // look for branch
if (!branch) { // if no branch, create one
branch = {"name": path, "type": "parent", "children": []}
tree.children.push(branch); // push this into children of current level
}
tree = branch; // set tree to branch before processing next item in path
}
tree.children.push(obj); // add the item to the hierarchy after path is exhausted
}
return root.children; // return children of the root to suit your output
}
function data() {
return [
{
path: ['Movies', 'Comedies', 'TopRanked'],
name: 'The Hangover',
id: '1',
},
{
path: ['Movies', 'Comedies', 'TopRanked'],
name: 'Eurotrip',
id: '2',
},
{
path: ['Movies', 'Action'],
name: 'Need for Speed',
id: '3',
},
{
path: ['Life'],
name: 'Not so bad',
id: '4',
},
{
path: ['Life', 'Financial', 'Income'],
name: 'Making Hundreds',
id: '5',
},
{
path: ['Life', 'Financial', 'Income'],
name: 'Making Thousands',
id: '6',
},
{
path: ['Life', 'MonthlySpent'],
name: 'Just a little bit',
id: '7',
},
{
path: ['Life', 'MonthlySpent'],
name: 'Living large',
id: '8',
},
];
}
.as-console-wrapper { max-height: 100% !important; top: 0; }
I am having a array in which some elements has type of farmer and some elements doesn't have the key type.I would like to check the array if all the elements in the array has type is farmer then i would set the value as sourceType as Farmer.
If the element doesn't have the key type in it then i will set the value as SourceType as Godown
If the element contains both (with the key as type and without the key type)then i would set the value as soruceType as Multiple
I have tried with some but i am getting the value as undefined can anyone tell me the fastest apprach where i can check all the elements in the array.
Structure:
[
{id: 2269, name: "Agalya", mobile_no: "1234567890", location: "Erode", type: "Farmer"},
{id: 2322, name: "andhra ", mobile_no: "0123456789", location: "Erode", type: "Farmer"},
{id: 2366, name: "edwin", mobile_no: "234567890", location: "Madurai", type: "Farmer"},
{id: 99, name: "G12", location: "Vgff", capacity: "5", user_id: 362},
{id: 126, name: "Pattrai A", location: "Erode", capacity: "88", user_id: 362}
]
var array = [
{id: 2269, name: "Agalya", mobile_no: "1234567890", location: "Erode", type: "Farmer"},
{id: 2322, name: "andhra ", mobile_no: "0123456789", location: "Erode", type: "Farmer"},
{id: 2366, name: "edwin", mobile_no: "234567890", location: "Madurai", type: "Farmer"},
{id: 99, name: "G12", location: "Vgff", capacity: "5", user_id: 362},
{id: 126, name: "Pattrai A", location: "Erode", capacity: "88", user_id: 362}
];
var SourceType = '';
let farmerArray = array.filter(function (item) {
return item.type && item.type === "Farmer";
});
let goDownArray = array.filter(function (item) {
return !item.type;
});
if (farmerArray && farmerArray.count != 0 && farmerArray.length == array.length) { SourceType = 'Farmer'; }
else if (goDownArray && goDownArray.count != 0 && goDownArray.length == array.length) { SourceType = 'Godown'; }
else { SourceType = 'Multiple'; }
Check below snippet. It is simple and short.
By changing array name from input1 to input2 or input3 in below code you can test all cases.
if( input1.every(element => element.type === "Farmer") ) {
console.log("Farmer");
}
else if(input1.every(element => ! element.hasOwnProperty("type"))) {
console.log("Godown");
}
else {
console.log("Multiple");
}
var input1 = [
{id: 2269, name: "Agalya", mobile_no: "1234567890", location: "Erode", type: "Farmer"},
{id: 2322, name: "andhra ", mobile_no: "0123456789", location: "Erode", type: "Farmer"},
{id: 2366, name: "edwin", mobile_no: "234567890", location: "Madurai", type: "Farmer"},
{id: 99, name: "G12", location: "Vgff", capacity: "5", user_id: 362},
{id: 126, name: "Pattrai A", location: "Erode", capacity: "88", user_id: 362}
];
var input2 = [
{id: 2269, name: "Agalya", mobile_no: "1234567890", location: "Erode", type: "Farmer"},
{id: 2322, name: "andhra ", mobile_no: "0123456789", location: "Erode", type: "Farmer"},
{id: 2366, name: "edwin", mobile_no: "234567890", location: "Madurai", type: "Farmer"},
{id: 99, name: "G12", location: "Vgff", capacity: "5", user_id: 362, type: "Farmer"},
{id: 126, name: "Pattrai A", location: "Erode", capacity: "88", user_id: 362, type: "Farmer"}
];
var input3 = [
{id: 2269, name: "Agalya", mobile_no: "1234567890", location: "Erode"},
{id: 2322, name: "andhra ", mobile_no: "0123456789", location: "Erode"},
{id: 2366, name: "edwin", mobile_no: "234567890", location: "Madurai"},
{id: 99, name: "G12", location: "Vgff", capacity: "5", user_id: 362},
{id: 126, name: "Pattrai A", location: "Erode", capacity: "88", user_id: 362}
];
if( input1.every(element => element.type === "Farmer") ) {
console.log("Farmer");
}
else if(input1.every(element => ! element.hasOwnProperty("type"))) {
console.log("Godown");
}
else {
console.log("Multiple");
}
Trying to generate dropdown with deep nested elements.
Incoming data:
111: {id: 111, name: '111' },
222: {id: 222, name: '222' },
333: {id: 333, name: '333', parent: {id: 222} },
444: {id: 444, name: '444', parent: {id: 333} },
555: {id: 555, name: '555' }
I know only parent and I want to generate a tree for React template.
It's going to be like this:
result:
[{
id: 111,
name: '111'
},
{
id: 222,
name: '222',
children: [{
id: 333,
name: '333',
parent: {
id: 222
},
children: [{
id: 444,
name: '444',
parent: {
id: 333
}
}]
}
]
},
{
id: 555,
name: '555'
}
]
You could take temporary object for keeping all references to the same id and build a tree with the parts.
This works for unsorted data as well.
var data = { 111: { id: 111, name: '111' }, 222: { id: 222, name: '222' }, 333: { id: 333, name: '333', parent: { id: 222 } }, 444: { id: 444, name: '444', parent: { id: 333 } }, 555: { id: 555, name: '555' } },
tree = function (object, root) {
var r = [], o = {};
Object.keys(object).forEach(function (k) {
var id = object[k].id;
o[id] = Object.assign(o[id] || {}, object[k]);
if (o[id].parent === root) {
r.push(o[id]);
} else {
o[o[id].parent.id] = o[o[id].parent.id] || {};
o[o[id].parent.id].children = o[o[id].parent.id].children || [];
o[o[id].parent.id].children.push(o[id]);
}
});
return r;
}(data, undefined);
console.log(tree);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I take a time for make a demo, but look at your object is worg no passed any json validator.
var _data = [{
id: '111',
name: '111'
}, {
id: '222',
name: '222',
children: [
{
id: '333',
name: '333',
parent: {
id: '222'
},
children: [
{
id: '444',
name: '444',
parent: {
id: '333'
}
}
]
}]
}
];
console.log(_data);
function make(arr){
var _arr = [];
function _do(arr, _parent){
for(var i=0; i<arr.length;i++){
var _o = {
id: arr[i].id,
name: arr[i].name
};
if(_parent){
_o.parent = _parent;
}
if(arr[i].children){
_do(arr[i].children, arr[i].id);
}
_arr[arr[i].id] = _o;
}
}
_do(arr);
return _arr
};
console.log(make(_data));
You can try following. You can solve n level nesting with it.
var obj = {
111: {id: 111, name: '111' },
222: {id: 222, name: '222' },
333: {id: 333, name: '333', parent: {id: 222} },
444: {id: 444, name: '444', parent: {id: 333} },
555: {id: 555, name: '555' }
};
// Iterate over the object keys and create the tree and only push items which have no parent in response
var response = [];
Object.keys(obj).forEach(function(key) {
var item = obj[key];
if (item.parent) {
obj[item.parent.id].children = obj[item.parent.id].children || [];
obj[item.parent.id].children.push(obj[key]);
} else {
response.push(obj[key]);
}
});
console.log(response);
I have a JSON object with nested children that I would like to flatten and modify using lodash. Ideally the revised JSON will have a value for the original level the nested children were at and show their original path.
Here is sample JSON:
var data = [
{id: 0, name: 'Australia', children: [
{id: 10, name: 'Melbourne', children: []},
{id: 11, name: 'Sydney', children: [
{id: 100, name: 'Surry Hills', children: []},
{id: 102, name: 'Darlinghurst', children: []}
]},
{id: 13, name: 'Kambalda', children: []}
]},
{id: 1, name: 'Spain', children: [
{id: 20, name: 'Barcelona', children: []},
{id: 21, name: 'Madrid', children: []}
]},
{id: 3, name: 'UK', children: [
{id: 30, name: 'London', children: [
{id: 302, name: 'Knightsbridge', children: []},
{id: 309, name: 'West End', children: []}
]},
{id: 33, name: 'Leeds', children: []},
{id: 35, name: 'Manchester', children: []}
]}
];
And the transformed JSON I would like to generate is:
[
{id: 0, name: 'Australia', level: 0, pathname: 'Australia'},
{id: 10, name: 'Melbourne', level: 1, pathname: 'Australia > Melbourne'},
{id: 11, name: 'Sydney', level: 1, pathname: 'Australia > Sydney'},
{id: 100, name: 'Surry Hills', level: 2, pathname: 'Australia > Sydney > Surry Hills'},
{id: 102, name: 'Darlinghurst', level: 2, pathname: 'Australia > Sydney > Darlinghurst'},
{id: 13, name: 'Kambalda', level: 1, pathname: 'Australia > Kambalda'},
{id: 1, name: 'Spain', level: 0, pathname: 'Spain'},
{id: 20, name: 'Barcelona', level: 1, pathname: 'Spain > Barcelona'},
{id: 21, name: 'Madrid', level: 1, pathname: 'Spain > Madrid'},
{id: 3, name: 'UK', level: 0, pathname: 'UK'},
{id: 30, name: 'London', level: 1, pathname: 'UK > London'},
{id: 302, name: 'Knightsbridge', level: 2, pathname: 'UK > London > Knightsbridge'},
{id: 309, name: 'West End', level: 2, pathname: 'UK > London > West End'},
{id: 33, name: 'Leeds', level: 1, pathname: 'UK > Leeds'},
{id: 35, name: 'Manchester', level: 1, pathname: 'UK > Manchester'}
]
I have been playing with _.chain, _.flatten and _.pluck and been unable to get anything close.
You can use a simple recursive helper function that would produce an array of arrays, and then use _.flattenDeep to flatten it. This does what you want:
function flattenMyTree(tree) {
function recurse(nodes, path) {
return _.map(nodes, function(node) {
var newPath = _.union(path, [node.name]);
return [
_.assign({pathname: newPath.join(' > '), level: path.length}, _.omit(node, 'children')),
recurse(node.children, newPath)
];
});
}
return _.flattenDeep(recurse(tree, []));
}
This one gives you the expected answer but it's not in ideal functional style;
The idea is to start flattening the array from outside. On each iteration of the while() loop the array is flattened by one level. Items of that level are appended to the result and children of those items are appended to the children array. In next iteration we flatten those children. When there are no more items in the children array it means that we finished processing the last level. Finally we sort items by pathname which effectively groups parents and their children together.
var current = data;
var level = 0;
var result = [];
var children = [];
while(current.length > 0) {
result = result.concat(_.map(current, function(item) {
if (!_.isArray(item.path)) {
item.path = [];
}
item.path.push(item.name);
children = children.concat(_.map(item.children, function(child) {
child.path = item.path.slice();
return child;
}));
item.level = level;
item.pathname = item.path.join(" > ");
delete item.path;
delete item.children;
return item;
}));
current = children;
children = [];
level++;
}
result.sort(function(a, b) {
return a.pathname.localeCompare(b.pathname);
});
console.log(result);
You can recursively reduce the data collection within a new collection array. Simply tap on each reduce callback for each item with the intention of recursively calling their descendants. The solution below makes use of lazy evaluation to add and remove specific fields.
DEMO
var data = [/*...*/];
function getPathHelper(collection, newCollection, path, level) {
return _.reduce(collection, function(newCollection, item) {
path += (path? ' > ': '') + item.name;
newCollection.push(
_(item)
.tap(_.partial(getPathHelper, item.children || [], newCollection, path, level + 1))
.omit('children')
.set('pathname', path)
.set('level', level)
.value()
);
return newCollection;
}, newCollection);
}
function getPath(collection) {
return _.sortBy(getPathHelper(collection, [], '', 0), 'pathname');
}
console.log(getPath(data));
This is based on #mik01aj's answer, but flattening in each step, and without the default empty array for the path parameter:
var data = [
{id: 0, name: 'Australia', children: [
{id: 10, name: 'Melbourne', children: []},
{id: 11, name: 'Sydney', children: [
{id: 100, name: 'Surry Hills', children: []},
{id: 102, name: 'Darlinghurst', children: []}
]},
{id: 13, name: 'Kambalda', children: []}
]},
{id: 1, name: 'Spain', children: [
{id: 20, name: 'Barcelona', children: []},
{id: 21, name: 'Madrid', children: []}
]},
{id: 3, name: 'UK', children: [
{id: 30, name: 'London', children: [
{id: 302, name: 'Knightsbridge', children: []},
{id: 309, name: 'West End', children: []}
]},
{id: 33, name: 'Leeds', children: []},
{id: 35, name: 'Manchester', children: []}
]}
];
function flattenMyTree(tree) {
function recurse(nodes, path) {
return _.flatMap(nodes, function(node) {
var newPath = _.union(path, [node.name]);
return _.concat([
_.assign({
pathname: newPath.join(' > '),
level: newPath.length - 1
},
_.omit(node, 'children'))
],
recurse(node.children, newPath)
);
});
}
return recurse(tree);
}
console.log(flattenMyTree(data));
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.11/lodash.min.js"></script>
EDIT: I've updated the data structure and new test here: http://jsfiddle.net/6Lwrsjou/5/ images2 is being nested under images, which it shouldn't be.
I have an array that contains objects like this:
var objects = [{
_id: 1,
name: 'images',
type: 'directory',
children: [{
_id: 2,
name: 'set 2',
type: 'directory',
children: [{
_id: 3,
name: 'image.jpg',
type: 'file'
},
{
_id: 4,
name: 'image2.jpg',
type: 'file'
},
{
_id: 5,
name: 'set 3',
type: 'directory',
children: [{
_id: 6,
name: 'image.jpg',
type: 'file'
},
{
_id: 7,
name: 'image2.jpg',
type: 'file'
}]
}]
}]
}]
What I want to do is based on the _id value, get a path to that object using the name value.
So for example, for _id: 6 the path would be images/set 3/
I have a fiddle http://jsfiddle.net/6Lwrsjou/2/ for what I've tried, but this doesn't work, it includes previous sets that are not parents.
var path = '';
function getDirectory(objects, id) {
_.each(objects, function(item) {
if (item._id == id) return false;
if (item.type === 'directory') {
if (path.length > 1) {
path += '/' + item.name;
} else {
path += item.name;
}
};
if (!_.isEmpty(item.children)) {
getDirectory(item.children, id);
}
});
}
getDirectory(objects, 7);
console.log(path);
Any ideas?
You need a little change your code, for find in all objects, something like this
var objects = [{
_id: 1,
name: 'images',
type: 'directory',
children: [{
_id: 2,
name: 'set 2',
type: 'directory',
children: [{
_id: 3,
name: 'image.jpg',
type: 'file'
},
{
_id: 4,
name: 'image2.jpg',
type: 'file'
},
{
_id: 5,
name: 'set 3',
type: 'directory',
children: [{
_id: 6,
name: 'image.jpg',
type: 'file'
},
{
_id: 7,
name: 'image2.jpg',
type: 'file'
}]
}]
}]
},{
_id: '1a',
name: 'images2',
type: 'directory',
children: [{
_id: '2a',
name: 'image2.jpg',
type: 'file'
}]
}]
function gd(arr, id, p){
var i,len,j, childlen;
console.log('gd:'+p);
for(i=0, len=arr.length; i<len;i++){
if(arr[i]._id == id) return p+'/'+ arr[i].name;
if(arr[i].children && arr[i].children.length >0){
var f = gd(arr[i].children,id,p+'/'+arr[i].name)
if(f) return f;
}
}
}
document.getElementById('result').innerHTML = gd(objects, '2a','');
<span id="result"></span>
var objects = [{
_id: 1,
name: 'images',
type: 'directory',
children: [{
_id: 2,
name: 'set 2',
type: 'directory',
children: [{
_id: 3,
name: 'image.jpg',
type: 'file'
},
{
_id: 4,
name: 'image2.jpg',
type: 'file'
},
{
_id: 5,
name: 'set 3',
type: 'directory',
children: [{
_id: 6,
name: 'image.jpg',
type: 'file'
},
{
_id: 7,
name: 'image2.jpg',
type: 'file'
}]
}]
}]
},{
_id: '1a',
name: 'images2',
type: 'directory',
children: [{
_id: '2a',
name: 'image2.jpg',
type: 'file'
}]
}]
function getDirectory(object, id){
var path="";
for(var i=0; i<object.length; i++){
if(object[i]._id == id) return object[i].name;
else{
if(typeof(object[i].children) !== "undefined"){
temp = getDirectory(object[i].children, id);
if(temp){
path += object[i].name+"/" + getDirectory(object[i].children, id);
return path;
}
}
}
}
return false;
}
path = getDirectory(objects, "6");
console.log(path);