Get path of an object tree - javascript

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);

Related

How to get list of parents id in json object

My nested json array looks like:
[
{
id: 1,
name: "Mike",
children: [
{ id: 2, name: "MikeC1" },
{ id: 3, name: "MikeC2" },
{
id: 4, name: "MikeC3",
children: [{ id: 5, name: "MikeCC1" }]
},
]
},
{
id: 6,
name: "Json",
children: [
{ id: 7, name: "JsonC1" },
{ id: 8, name: "JsonC2" },
{
id: 9, name: "JsonC3",
children: [{ id: 10, name: "JsonCC1" },{ id: 11, name: "JsonCC2" }]
},
]
}
]
Now I get a id like "11"
then get the parent ids array in json like [6,9,11]
How to do?
var id = 11
console.log(findParent(id))
//result is [6,9,11]
You need to do recursive search
const persons = [
{
id: 1,
name: "Mike",
children: [
{ id: 2, name: "MikeC1" },
{ id: 3, name: "MikeC2" },
{
id: 4, name: "MikeC3",
children: [{ id: 5, name: "MikeCC1" }]
},
]
},
{
id: 6,
name: "Json",
children: [
{ id: 7, name: "JsonC1" },
{ id: 8, name: "JsonC2" },
{
id: 9, name: "JsonC3",
children: [{ id: 10, name: "JsonCC1" },{ id: 11, name: "JsonCC2" }]
},
]
}
];
function searchRecursive(items, id) {
const allIds = [];
items.forEach(item => {
if(item.id === id) {
allIds.push(item.id);
}
else if(item.children) {
const ids = searchRecursive(item.children, id);
if(ids.length) allIds.push(item.id);
ids.forEach(id => allIds.push(id));
}
});
return allIds;
}
console.log(searchRecursive(persons, 11));

How to convert a list in to categories with sub items in javascript? [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 1 year ago.
Improve this question
How to convert the following in to expectedResult?
const items = [
{ type: 'fruits', index: 1, name: 'FRUITS' },
{ type: 'fruit', index: 2, name: 'orange' },
{ type: 'fruit', index: 3, name: 'apple' },
{ type: 'fruit', index: 4, name: 'banana' },
{ type: 'vegetables', index: 5, name: 'VEGETABLES' },
{ type: 'vegetable', index: 6, name: 'ginger' },
{ type: 'vegetable', index: 7, name: 'tomato' },
{ type: 'vegetable', index: 8, name: 'potato' },
{ type: 'vegetable', index: 9, name: 'garlic' },
{ type: 'vegetable', index: 10, name: 'cucumber' },
{ type: 'meats', index: 11, name: 'MEATS' },
{ type: 'meat', index: 12, name: 'Beef' },
{ type: 'meat', index: 13, name: 'Chicken' },
...so on
];
In to this:
const expectedResult = [
{
type: 'fruits',
index: 1,
name: 'FRUITS',
items: [
{ type: 'fruit', index: 2, name: 'orange' },
{ type: 'fruit', index: 3, name: 'apple' },
{ type: 'fruit', index: 4, name: 'banana' },
],
},
{
type: 'vegetables',
index: 5,
name: 'VEGETABLES',
items: [
{ type: 'vegetable', index: 6, name: 'ginger' },
{ type: 'vegetable', index: 7, name: 'tomato' },
{ type: 'vegetable', index: 8, name: 'potato' },
{ type: 'vegetable', index: 9, name: 'garlic' },
{ type: 'vegetable', index: 10, name: 'cucumber' },
],
},
{
type: 'meats',
index: 11,
name: 'MEATS',
items: [
{ type: 'meat', index: 12, name: 'Beef' },
{ type: 'meat', index: 13, name: 'Chicken' },
],
},
...so on
];
So the idea is to look for the type and then look for the next indices till the index changes the type and move those indices in to a sub array like items: []
for example for item type: 'fruits' check for the consecutive proceeding indices till type changes to type: 'vegetables' and copy those in-between indices in to type: 'fruits', items: [... in to this array] array.
**Idea is to look for consecutive in-between indices and copy them in to items: [] sub array.
Loop through the items. Set the current item type and add the item to the expected list. Subsequent items that match that type get added to that item. Repeat otherwise.
const items = [
{ type: 'fruits', index: 1, name: 'FRUITS' },
{ type: 'fruit', index: 2, name: 'orange' },
{ type: 'fruit', index: 3, name: 'apple' },
{ type: 'fruit', index: 4, name: 'banana' },
{ type: 'vegetables', index: 5, name: 'VEGETABLES' },
{ type: 'vegetable', index: 6, name: 'ginger' },
{ type: 'vegetable', index: 7, name: 'tomato' },
{ type: 'vegetable', index: 8, name: 'potato' },
{ type: 'vegetable', index: 9, name: 'garlic' },
{ type: 'vegetable', index: 10, name: 'cucumber' },
{ type: 'meats', index: 11, name: 'MEATS' },
{ type: 'meat', index: 12, name: 'Beef' },
{ type: 'meat', index: 13, name: 'Chicken' },
];
var type, expected = [];
for(var i = 0; i < items.length; i++) {
if(type && 0 <= type.indexOf(items[i].type)) {
expected[expected.length - 1].items.push(items[i]);
} else {
type = items[i].type;
items[i].items = [];
expected.push(items[i]);
}
}
console.log(expected);
You can divide these items into two arrays like this and then go from there...
const items = [
{ type: 'fruits', index: 1, name: 'FRUITS' },
{ type: 'fruit', index: 2, name: 'orange' },
{ type: 'fruit', index: 3, name: 'apple' },
{ type: 'fruit', index: 4, name: 'banana' },
{ type: 'vegetables', index: 5, name: 'VEGETABLES' },
{ type: 'vegetable', index: 6, name: 'ginger' },
{ type: 'vegetable', index: 7, name: 'tomato' },
{ type: 'vegetable', index: 8, name: 'potato' },
{ type: 'vegetable', index: 9, name: 'garlic' },
{ type: 'vegetable', index: 10, name: 'cucumber' },
{ type: 'meat', index: 11, name: 'MEAT' },
{ type: 'vegetable', index: 12, name: 'Beef' },
{ type: 'vegetable', index: 13, name: 'Chicken' },
];
const group = items.reduce((acc,it)=>{
if(it.type.toUpperCase()===it.name){
acc.cats.push(it)
}else{
acc.subs.push(it)
}
return acc;
},{cats:[],subs:[]});
console.log(group)
You can use a reduce function to reformat your object:
items.reduce((acc, item) => {
const availableTypes = Object.keys(acc)
if (!acc[item.type] && !availableTypes.some((type) => type.indexOf(item.type) === 0)) {
return acc[item.type] = { ...item, items: [] }
} else {
const currentType = availableTypes.find((type) => type.indexOf(item.type) === 0)
return acc[currentType] = {
...acc[currentType],
items: acc[currentType].items.concat(item)
}
}
}, {})
const items = [
{ type: 'fruits', index: 1, name: 'FRUITS' },
{ type: 'fruit', index: 2, name: 'orange' },
{ type: 'fruit', index: 3, name: 'apple' },
{ type: 'fruit', index: 4, name: 'banana' },
{ type: 'vegetables', index: 5, name: 'VEGETABLES' },
{ type: 'vegetable', index: 6, name: 'ginger' },
{ type: 'vegetable', index: 7, name: 'tomato' },
{ type: 'vegetable', index: 8, name: 'potato' },
{ type: 'vegetable', index: 9, name: 'garlic' },
{ type: 'vegetable', index: 10, name: 'cucumber' },
{ type: 'meats', index: 11, name: 'MEAT' },
{ type: 'meat', index: 12, name: 'Beef' },
{ type: 'meat', index: 13, name: 'Chicken' }
];
let expectedResult = [];
items.forEach(function(element){
var parentItem = expectedResult.findIndex(ele => ele.type == element.type+'s' );
if( parentItem >= 0){
expectedResult[parentItem].items.push(element);
return;
}
element.items = [];
expectedResult.push(element);
});
console.log(expectedResult);

How to convert flat array containing a child "path" array into a nested array of objects based on the path?

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; }

Cannot Push Data Children of Children in Nested Array

So I have the following Array. I can push to the parent level and child level however I can't seem to push to Children of Children or deeper. Here's the Array:
TREE_DATA: SegmentCategory[] = [
{
id: 1,
name: 'Category 1',
children: [
{
id: 2,
name: 'Category 1-1',
children: [
{
id: 4,
name: 'Category 1-1-a',
children: []
},
{
id: 5,
name: 'Category 1-1-b',
children: []
},
]
},
{
id: 6,
name: 'Category 1-2',
children: [
{
id: 7,
name: 'Category 1-2-a',
children: [
{
id: 8,
name: 'Category 1-2-1',
children: [
{
id: 9,
name: 'Category 1-2-2-a',
children: []
}
]
}
]
}
]
}
]
},
{
id: 10,
name: 'Category 2',
children: []
}
];
And here is the method I am using to add data to the array:
createCategory() {
this.id++;
if (this.formGroup.controls['parentCategory'].value == null) {
this.TREE_DATA.push(
{
id: this.id,
name: this.formGroup.controls['categoryName'].value,
children: []
});
} else {
this.TREE_DATA.forEach((v, index) => {
if (v.id === this.formGroup.controls['parentCategory'].value.id) {
this.TREE_DATA[index].children.push(
{
id: this.id,
name: this.formGroup.controls['categoryName'].value,
children: []
}
);
}
});
}
}
I know that I can just add an additional foreach however this doesn't seem like the best way to handle this, as I could want to go 5-6 layers down at any time to add a new nested child. What would be the best way to handle this?
You could take a recursive function for finding an object in the tree.
function find(array, id) {
var result;
array.some(object => {
if (object.id === id) return result = object;
return result = find(object.children || [], id);
});
return result;
}
var data = [{ id: 1, name: 'Category 1', children: [{ id: 2, name: 'Category 1-1', children: [{ id: 4, name: 'Category 1-1-a', children: [] }, { id: 5, name: 'Category 1-1-b', children: [] }] }, { id: 6, name: 'Category 1-2', children: [{ id: 7, name: 'Category 1-2-a', children: [{ id: 8, name: 'Category 1-2-1', children: [{ id: 9, name: 'Category 1-2-2-a', children: [] }] }] }] }] }, { id: 10, name: 'Category 2', children: [] }];
console.log(find(data, 9));
console.log(find(data, 'unknown'));

deep filter a dynamic array of objects (by string property)

I wanna filter an dynamic array by the property "name".
The array has 3 kinds of objects: "link", "folder" & "app".
{
type: "link",
name: ""
},
{
type: "folder",
name: "",
childs: []
},
{
type: "app",
name: "",
childs: []
}
app can only have links as children.
folder can have all 3 kinds (link, folder, app) as children.
If a child matches the search value, all parents of it survive.
(its siblings get filtered out if it they don't match)
I have an array somewhat like this:
var items = [
{
type: 'app',
name: 'bar',
childs: [
{
type: 'link',
name: 'mee'
}
]
},
{
type: 'folder',
name: 'fizz',
childs: [
{
type: 'link',
name: 'buzz'
},
{
type: 'app',
name: 'boo',
childs: []
}
]
},
{
type: 'folder',
name: 'bla',
childs: [
{
type: 'app',
name: 'blee',
childs: [
{
type: 'link',
name: 'blee'
},
{
type: 'link',
name: 'bar'
}
]
},
{
type: 'folder',
name: 'mee',
childs: [
{
type: 'app',
name: 'bar',
childs: [
{
type: 'link',
name: 'maa'
}
]
},
{
type: 'link',
name: 'mee'
}
]
}
]
},
{
type: 'link',
name: 'foo',
childs: [
{
type: 'folder',
name: 'baf',
childs: []
}
]
}
];
I managed to write a non deep filtering:
https://jsfiddle.net/6ocd5u8y
(I would appreciate any suggestions for this code as well)
I also found code which filters deep, but keeps the siblings of the matching children alive and filters by any string:
https://jsfiddle.net/ndfu8cpL
the result for a search like 'bar' should be:
filteredArray = [
{
type: 'app',
name: 'bar',
childs: []
},
{
type: 'folder',
name: 'bla',
childs: [
{
type: 'app',
name: 'blee',
childs: [
{
type: 'link',
name: 'bar'
}
]
},
{
type: 'folder',
name: 'mee',
childs: [
{
type: 'app',
name: 'bar',
childs: []
}
]
}
]
}
]
I would be very grateful if someone could help me out.
You could build new object if the wanted property is found or if the nested array has some items after filtering.
function filter(array, name) {
return array.reduce((r, o) => {
var childs = filter(o.childs || [], name);
if (o.name === name || childs.length) r.push(Object.assign({}, o, { childs }));
return r;
}, []);
}
var items = [{ type: 'app', name: 'bar', childs: [{ type: 'link', name: 'mee' }] }, { type: 'folder', name: 'fizz', childs: [{ type: 'link', name: 'buzz' }, { type: 'app', name: 'boo', childs: [] }] }, { type: 'folder', name: 'bla', childs: [{ type: 'app', name: 'blee', childs: [{ type: 'link', name: 'blee' }, { type: 'link', name: 'bar' }] }, { type: 'folder', name: 'mee', childs: [{ type: 'app', name: 'bar', childs: [{ type: 'link', name: 'maa' }] }, { type: 'link', name: 'mee' }] }] }, { type: 'link', name: 'foo', childs: [{ type: 'folder', name: 'baf', childs: [] }] }];
console.log(filter(items, 'bar'));
.as-console-wrapper { max-height: 100% !important; top: 0; }

Categories

Resources