flatten javascript array of objects - javascript

I have an array of object with hierarchical structure, something like this:
[
{name: 'ParentOne', children: [
{name: 'ParentOneChildOne'},
{name: 'ParentOneChildTwo', children: [
{name: 'ParentOneChildTwoGrandChildOne'},
]},
]},
{name: 'ParentTwo', children: [
{name: 'ParentTwoChildOne', children: [
{name: 'ParentTwoChildOneGrandChildOne'},
{name: 'ParentTwoChildOneGrandChildTwo'}
]},
{name: 'ParentTwoChildTwo'}
]}
];
I want to flatten it:
[
{name: 'ParentOne'},
{name: 'ParentOneChildOne'},
{name: 'ParentOneChildTwo'},
{name: 'ParentOneChildTwoGrandChildOne'},
{name: 'ParentTwo'},
{name: 'ParentTwoChildOne'},
{name: 'ParentTwoChildOneGrandChildOne'},
{name: 'ParentTwoChildOneGrandChildTwo'},
{name: 'ParentTwoChildTwo'}
]
I have tried _.flatten() and _.flatMap(), but it does not produce what I need. What is the best way to achieve it preferably using lodash.js or underscore.js.

No need for underscore/lodash.
const arr = [
{name: 'ParentOne', children: [
{name: 'ParentOneChildOne'},
{name: 'ParentOneChildTwo', children: [
{name: 'ParentOneChildTwoGrandChildOne'},
]},
]},
{name: 'ParentTwo', children: [
{name: 'ParentTwoChildOne', children: [
{name: 'ParentTwoChildOneGrandChildOne'},
{name: 'ParentTwoChildOneGrandChildTwo'}
]},
{name: 'ParentTwoChildTwo'}
]}
];
function flatten(arr) {
return arr? arr.reduce((result, item) => [
...result,
{ name: item.name },
...flatten(item.children)
], []) : [];
}
console.log(flatten(arr));

Recursive functions is the way to go for any depth of iteration.
With some ES2015 and LoDash/Underscore
var arr = [{
name: 'ParentOne',
children: [{
name: 'ParentOneChildOne'
}, {
name: 'ParentOneChildTwo',
children: [{
name: 'ParentOneChildTwoGrandChildOne'
}, ]
}, ]
}, {
name: 'ParentTwo',
children: [{
name: 'ParentTwoChildOne',
children: [{
name: 'ParentTwoChildOneGrandChildOne'
}, {
name: 'ParentTwoChildOneGrandChildTwo'
}]
}, {
name: 'ParentTwoChildTwo'
}]
}];
var res = _.reduce(arr, (a, b) => {
(rec = item => {
_.each(item, (v, k) => (_.isObject(v) ? rec(v) : a.push(_.zipObject([k], [v]))))
})(b);
return a;
}, []);
console.log(res);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>

I would use .reduce and recursion to do this. Here's my implementation using Array.reduce, but you could do much the same with underscore's reduce function.
const arr = [
{name: 'ParentOne', children: [
{name: 'ParentOneChildOne'},
{name: 'ParentOneChildTwo', children: [
{name: 'ParentOneChildTwoGrandChildOne'},
]},
]},
{name: 'ParentTwo', children: [
{name: 'ParentTwoChildOne', children: [
{name: 'ParentTwoChildOneGrandChildOne'},
{name: 'ParentTwoChildOneGrandChildTwo'}
]},
{name: 'ParentTwoChildTwo'}
]}
];
function flatten(arr) {
return arr.reduce((result, current) => {
if (current.children) {
const children = flatten(current.children);
delete current.children;
result.push(current);
result.push(...children);
} else {
result.push(current);
}
return result;
}, [])
}
console.log(flatten(arr));

You could try adapting the flatten function given in this answer, and slightly twist the logic to your object's structure.
//Your object
var data = [{
name: 'ParentOne',
children: [{
name: 'ParentOneChildOne'
},
{
name: 'ParentOneChildTwo',
children: [{
name: 'ParentOneChildTwoGrandChildOne'
}, ]
},
]
},
{
name: 'ParentTwo',
children: [{
name: 'ParentTwoChildOne',
children: [{
name: 'ParentTwoChildOneGrandChildOne'
},
{
name: 'ParentTwoChildOneGrandChildTwo'
}
]
},
{
name: 'ParentTwoChildTwo'
}
]
}
];
//georg's flatten function
flatten = function(x, result, prefix) {
if (_.isObject(x)) {
_.each(x, function(v, k) {
flatten(v, result, prefix ? prefix + '_' + k : k)
})
} else {
result[prefix] = x
}
return result
}
//using the function on your data
result = flatten(data, {});
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
Does this help at all?

You could use some destruction and use a recursive function for collecting all wanted items.
var array = [{ name: 'ParentOne', children: [{ name: 'ParentOneChildOne' }, { name: 'ParentOneChildTwo', children: [{ name: 'ParentOneChildTwoGrandChildOne' },] },] }, { name: 'ParentTwo', children: [{ name: 'ParentTwoChildOne', children: [{ name: 'ParentTwoChildOneGrandChildOne' }, { name: 'ParentTwoChildOneGrandChildTwo' }] }, { name: 'ParentTwoChildTwo' }] }],
flat = (r, { name, children = [] }) => [...r, { name }, ...children.reduce(flat, []) ],
result = array.reduce(flat, []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
On EDGE, you need a different use of default values.
var array = [{ name: 'ParentOne', children: [{ name: 'ParentOneChildOne' }, { name: 'ParentOneChildTwo', children: [{ name: 'ParentOneChildTwoGrandChildOne' },] },] }, { name: 'ParentTwo', children: [{ name: 'ParentTwoChildOne', children: [{ name: 'ParentTwoChildOneGrandChildOne' }, { name: 'ParentTwoChildOneGrandChildTwo' }] }, { name: 'ParentTwoChildTwo' }] }],
flat = (r, { name, children }) => [...r, { name }, ...(children || []).reduce(flat, []) ],
result = array.reduce(flat, []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Related

multilevel object traversing and print value based on key

Here is my object. There could be children inside of children and so on.
I Am trying to print only name for all of them.
Here is JSFiddle
var products = [
{
name: 'Allison',
children: [
{
name: 'John',
children: [
{
name: 'Scott',
children: [],
},
],
},
{
name: 'Sarah',
children: [],
},
]
},
{
name: 'Tony',
children: [
{
name: 'Lucy',
children: [],
}
]
}
This is what I have tried so far, how can I Print name of all children regardless of what level they are in the object??
for(var i = 0; i < products.length; i++)
{
console.log(products[i].name);
if(products[i].children.length > 0) {
console.log(products[i].children);
// Print only name of all children.
}
}
You can use recursive technical.
var products = [ { name: 'Allison', children: [{ name: 'John', children: [ { name: 'Scott', children: [],},],},{ name: 'Sarah', children: [],},]},{name: 'Tony',children: [{name: 'Lucy',children: [],}]}];
const printRecursively = (products) => {
for (const k of products)
{
console.log(k.name);
k.children.length > 0 && printRecursively(k.children); // Recurive here.
}
}
printRecursively(products);
Here is the implementation, please check
Also here
var products = [
{
name: 'Allison',
children: [
{
name: 'John',
children: [
{
name: 'Scott',
children: [],
},
],
},
{
name: 'Sarah',
children: [],
},
]
},
{
name: 'Tony',
children: [
{
name: 'Lucy',
children: [],
}
]
}
];
function printChildrenNames(children) {
for(var i = 0; i < children.length; i++) {
console.log(children[i].name);
if(children[i].children.length > 0) {
printChildrenNames(children[i].children);
}
}
}
printChildrenNames(products)
Here is an iterative solution using object-scan. For your use case a simple recursive solution might be the best choice, however this solution is very clean and easily adjustable when requirements change.
// const objectScan = require('object-scan');
const data = [{ name: 'Allison', children: [{ name: 'John', children: [{ name: 'Scott', children: [] }] }, { name: 'Sarah', children: [] }] }, { name: 'Tony', children: [{ name: 'Lucy', children: [] }] }];
console.log(objectScan(['**.name'], { rtn: 'value', reverse: false })(data));
// => [ 'Allison', 'John', 'Scott', 'Sarah', 'Tony', 'Lucy' ]
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/object-scan#14.0.0"></script>
Disclaimer: I'm the author of object-scan

Remove items from nested array of objects based on a condition

In my application, I have data returned from the server like below. It has very deep nesting:
var data = [{
name: "root",
children: [{
name: "Parent1",
children: [{
name: "Parent1-child1",
children: [{
name: "Parent1-child1-grandchild1",
children: [{
name: "Parent1-child1-grandchild1-last",
children:[]
}]
},
{
name: "Parent1-child1-grandchild2",
children: []
},
{
name: "Parent1-child1-grandchild3",
children: []
}
]
},
{
name: "Paren1-child2",
children: [{
name: "Parent1-chil2-grandchild1",
children: []
},
{
name: "Parent1-child2-grandchild2",
children: [{
name: "Parent1-child2-grandchild2-last",
children: []
}]
},
{
name: "Parent1-child2-grandchild3",
children: []
}
]
},
{
name: "Parent1-child3",
children: []
}
]
},
{
name: "Parent2",
children: [{
name: "Parent2-child1",
children: []
},
{
name: "Parent2-child2",
children: [{
name: "Parent2-child2-grandchild1",
children: []
},
{
name: "Parent2-child2-grandchild2",
children: [{
name: "Parent2-child2-grandchild2-last",
children: []
}]
}
]
}
]
},
{
name: "Parent3",
children: []
}
]
}];
The requirement is to loop through all the objects (deep nested level also) and remove the object if the children property has a value of an empty array. So the output should be like below
var data = [{
name: "root",
children: [{
name: "Parent1",
children: [{
name: "Parent1-child1",
children: [{
name: "Parent1-child1-grandchild1",
children: []
},
]
},
{
name: "Paren1-child2",
children: [
{
name: "Parent1-child2-grandchild2",
children: []
},
]
},
]
},
{
name: "Parent2",
children: [
{
name: "Parent2-child2",
children: [
{
name: "Parent2-child2-grandchild2",
children: []
}
]
}
]
}
]
}];
I have tried the following code, but it doesn't work as expected. Please let me know how to achieve the expected result.
function checkChildrens(arr) {
arr.forEach((ele,i) => {
if(ele.hasOwnProperty('children')) {
checkChildrens(ele['children'])
} else {
arr.splice(i,1)
}
})
}
checkChildrens(data);
I have tried with the filter method also in that case. It is not working correctly.
arr.filter((ele,i)=>{
if(ele.hasOwnProperty('children') && ele.children.length !== 0 ){
removeEmpty(ele.children)
}else{
return false;
}
return true;
})
You could rebuild new objects by checking the children array length.
function filter(array) {
return array.reduce((r, o) => {
if (o.children && o.children.length) {
r.push(Object.assign({}, o, { children: filter(o.children) }));
}
return r;
}, []);
}
var data = [{ name: "root", children: [{ name: "Parent1", children: [{ name: "Parent1-child1", children: [{ name: "Parent1-child1-grandchild1", children: [{ name: "Parent1-child1-grandchild1-last", children: [] }] }, { name: "Parent1-child1-grandchild2", children: [] }, { name: "Parent1-child1-grandchild3", children: [] }] }, { name: "Paren1-child2", children: [{ name: "Parent1-chil2-grandchild1", children: [] }, { name: "Parent1-child2-grandchild2", children: [{ name: "Parent1-child2-grandchild2-last", children: [] }] }, { name: "Parent1-child2-grandchild3", children: [] }] }, { name: "Parent1-child3", children: [] }] }, { name: "Parent2", children: [{ name: "Parent2-child1", children: [] }, { name: "Parent2-child2", children: [{ name: "Parent2-child2-grandchild1", children: [] }, { name: "Parent2-child2-grandchild2", children: [{ name: "Parent2-child2-grandchild2-last", children: [] }] }] }] }, { name: "Parent3", children: [] }] }],
result = filter(data);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Approach for removing all nested empty children (except the last one. This has an empty object, but no children property).
function filter(array) {
return array.reduce((r, o) => {
if (o.children) {
var children = filter(o.children);
if (children.length) r.push(Object.assign({}, o, { children }));
} else {
r.push(o);
}
return r;
}, []);
}
var data = [{ name: "root", children: [{ name: "Parent1", children: [{ name: "Parent1-child1", children: [{ name: "Parent1-child1-grandchild1", children: [{ name: "Parent1-child1-grandchild1-last", children: [] }] }, { name: "Parent1-child1-grandchild2", children: [] }, { name: "Parent1-child1-grandchild3", children: [] }] }, { name: "Paren1-child2", children: [{ name: "Parent1-chil2-grandchild1", children: [] }, { name: "Parent1-child2-grandchild2", children: [{ name: "Parent1-child2-grandchild2-last", children: [] }] }, { name: "Parent1-child2-grandchild3", children: [] }] }, { name: "Parent1-child3", children: [] }] }, { name: "Parent2", children: [{ name: "Parent2-child1", children: [] }, { name: "Parent2-child2", children: [{ name: "Parent2-child2-grandchild1", children: [] }, { name: "Parent2-child2-grandchild2", children: [{ name: "Parent2-child2-grandchild2-last", children: [] }] }] }] }, { name: "Parent3", children: [{}] }] }],
result = filter(data);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
var data = [{
name: "root",
children: [{
name: "Parent1",
children: [{
name: "Parent1-child1",
children: [{
name: "Parent1-child1-grandchild1",
children: [{
name: "Parent1-child1-grandchild1-last",
children: []
}]
},
{
name: "Parent1-child1-grandchild2",
children: []
},
{
name: "Parent1-child1-grandchild3",
children: []
}
]
},
{
name: "Paren1-child2",
children: [{
name: "Parent1-chil2-grandchild1",
children: []
},
{
name: "Parent1-child2-grandchild2",
children: [{
name: "Parent1-child2-grandchild2-last",
children: []
}]
},
{
name: "Parent1-child2-grandchild3",
children: []
}
]
},
{
name: "Parent1-child3",
children: []
}
]
},
{
name: "Parent2",
children: [{
name: "Parent2-child1",
children: []
},
{
name: "Parent2-child2",
children: [{
name: "Parent2-child2-grandchild1",
children: []
},
{
name: "Parent2-child2-grandchild2",
children: [{
name: "Parent2-child2-grandchild2-last",
children: []
}]
}
]
}
]
},
{
name: "Parent3",
children: []
}
]
}];
function checkChildrens(arr) {
let res = []
arr.forEach(v => {
if (v.children && v.children.length) {
res = res.concat({
name: v.name,
children: checkChildrens(v.children)
})
}
})
return res
}
console.log(checkChildrens(data));

Filter nested object and keep parents

I want to search an nested object by values of property 'name' and the result will keep its all parents.
For example,
const object = [
{
name: 'Mary',
children: [
{
name: 'Jack',
},
{
name: 'Kevin',
children: [
{
name: 'Lisa',
}
]
}
]
},
{
name: 'Gina',
children: [
{
name: 'Jack',
}
]
}
]
If I search 'Mary', it should be return:
[
{
name: 'Mary',
}
]
If I search 'Jack', it should be return:
[
{
name: 'Mary',
children: [
{
name: 'Jack',
}
]
},
{
name: 'Gina',
children: [
{
name: 'Jack',
}
]
}
]
If I search 'Lisa', it should be return:
[
{
name: 'Mary',
children: [
{
name: 'Jack',
children: [
{
name: 'Lisa',
}
]
}
]
}
]
I tried some methods but I could only filter two layer. As below:
return object.filter(data => {
if (data.children) {
return data.name.includes(keyword) || data.children.find(item => item.name.includes(keyword));
}
return data.name.includes(keyword);
})
Could someone point me in the right direction? Thanks!
You could build an object and if nested, check the children and create the parents, if necessary.
function getObjects(array, target) {
return array.reduce((r, { name, children = [] }) => {
if (name === target) {
r.push({ name });
return r;
}
children = getObjects(children, target);
if (children.length) {
r.push({ name, children })
}
return r;
}, []);
}
var data = [{ name: 'Mary', children: [{ name: 'Jack' }, { name: 'Kevin', children: [{ name: 'Lisa' }] }] }, { name: 'Gina', children: [{ name: 'Jack' }] }];
console.log(getObjects(data, 'Mary'));
console.log(getObjects(data, 'Jack'));
console.log(getObjects(data, 'Lisa'));
.as-console-wrapper { max-height: 100% !important; top: 0; }
Here is an example of a depth-first approach:
function searchWithParents(tree, query) {
let results = [];
for (const {name, children} of tree) {
if (name === query) {
results.push({name});
}
if (children) {
const subtreeResults = searchWithParents(children, query);
const mappedResults = subtreeResults.map(child => ({name, children: [child]}))
results = results.concat(mappedResults);
}
}
return results;
}
console.log(searchWithParents(object, 'Mary'));
console.log(searchWithParents(object, 'Jack'));
console.log(searchWithParents(object, 'Lisa'));

Accessing nested array in a json

let ages = data
.filter(isDog)
.map(dogYears)
.reduce(sum);
mL/hr
i want to find the best way of accessing array elements in a javascript object.
Eg: I want to find the first faculty name & first specializations for each course.
var students =
{
deptartment:[
{
name:'Computer Science',
age:20,
Course:[
{ id: 100000
name:'Object Oriented Programming',
faculty:[
{
id:123,
name:'John',
Specialization: [
{name: 'science'},
{name: 'Physics'}
]
}
]
},
{ id: 100001
name:'C#',
faculty:[
{
id:124,
name:'Denis',
Specialization: [
{name: 'Ecnonomics'},
{name: 'Physics'}
]
}
]
}
],
}
]
};
I know i can get the faculty name and specialization by
var courses= deptartment && deptartment.Course ;
var facultyWithSpecialization= {};
if(courses){
courses.forEach(course =>{
var fname = course.faculty && course.faculty[0].name;
var s= course.faculty && course.faculty.Specialization;
facultyWithSpecialization[fname] = s && s[0].name;
})
}
use Object.assign({}, deptartment.Course) instead of department.Course
tried to use the below code but it doesn't make much difference.
var courses=Object.values(Object.assign({}, deptartment.Course));
var fname = Object.values(Object.assign({}, course.faculty[0].Specialization[0]));
Expecting
'John': 'science'
'Denis': 'Ecnonomics'
You can try this. There were many error in the object including spelling mistakes and formatting
var students = {
deptartment: [{
name: 'Computer Science',
age: 20,
Course: [{
id: 100000,
name: 'Object Oriented Programming',
faculty: [{
id: 123,
name: 'John',
Specialization: [{
name: 'science'
},
{
name: 'Physics'
}
]
},
{
id: 124,
name: 'Denis',
Specialization: [{
name: 'Ecnonomics'
},
{
name: 'Physics'
}
]
}
]
}],
}]
}
var obj = {};
students.deptartment.forEach((e) => {
e.Course.forEach((k) => {
k.faculty.forEach((l) => {
obj[l.name] = l.Specialization[0].name
})
})
})
console.log(obj)
I think you meant department instead of deptartment.
I modified a bit your JSON as it was a bit buggy:
var students = {
departments:[
{
name:'Computer Science',
age:20,
Courses:[
{ id: 100000,
name:'Object Oriented Programming',
faculty:[
{
id:123,
name:'John',
Specialization: [
{name: 'science'},
{name: 'Physics'}
]
},
{
id:124,
name:'Denis',
Specialization: [
{name: 'Ecnonomics'},
{name: 'Physics'}
]
}
]
}
],
}]
}
You can use map to achieve this nesting:
students.departments.map(
department => department.Courses.map(
course => course.faculty.map(
student => ({
name: student.name,
specialization: student.Specialization[0].name // check nulls here!
})
)
)
)

flatten an array of objects in javascript

I have a data structure that looks like this
const array = [{
name: 'bar',
children: [{
name: 'foo',
children: [{
name: 'baz123',
}, {
name: 'baz',
}]
}]
}, {
name: 'shallowKey'
}, {
name: 'abc'
}];
And I would like to flatten it to look something like this
[{
name: 'bar'
}, {
name: 'foo',
}, {
name: 'baz123',
}, {
name: 'baz',
}, {
name: 'shallowKey'
}, {
name: 'abc'
}];
I tried lodash like this https://jsfiddle.net/hmzhjji/081q60qg/1/
But it's not doing anything, any other way I can do this?
Thanks
A recursive way would be:
function flatten(array, result = []){
for(const {name, children} of array){
result.push({name});
if(children) flatten(children, result);
}
return result;
}
Or the alternative ES6 version:
const flatten = array => array.reduce((res, {name, children = []}) => res.concat(name).concat(flatten(children)), []);
So you can do flatten(array) to get the desired result.
You can use forEach to iterate over the array and check if the required object is present and call the function recursively
const array = [{
name: 'bar',
children: [{
name: 'foo',
children: [{
name: 'baz123',
}, {
name: 'baz',
}]
}]
}, {
name: 'shallowKey'
}, {
name: 'abc'
}];
var res = [];
function flatten(array){
array.forEach(function(obj){
var name = {name: obj.name}
res.push(name);
if(obj.children){
flatten(obj.children)
}
})
return res;
}
console.log(flatten(array))

Categories