I have the following array
{
"id": "111",
"name": "1111",
"children": [
{
"id": "22222",
"name": "2222",
"children": [
{
"id": "AAAA",
"name": "AAAA",
"children": [
{
"id": "DDD",
"name": "DDD"
},
{
"id": "EEE",
"name": "EEE"
}
]
},
{
"id": "BBBB",
"name": "BBB",
"children": [
{
"id": "FFF",
"name": "FFF"
},
{
"id": "GGG",
"name": "GGG",
"children": [
{
"id": "7777",
"name": "7777"
},
{
"id": "8888",
"name": "8888"
}
]
}
]
}
]
}
]
}
And I would like to create an array with the parents of a child by its ID.
So for example if I wanted to get the path to the child with ID "FFF", then the array would look like something like this:
["1111", "2222", "BBB", "FFF"]
How could I go about doing that?
You could take an iterative and recursive approach.
function getItems({ children, ...object }, key, value) {
var temp;
if (object[key] === value) return [object];
if (children) children.some(o => temp = getItems(o, key, value));
return temp && [object, ...temp];
}
var data = { id: "111", name: "1111", children: [{ id: "22222", name: "2222", children: [{ id: "AAAA", name: "AAAA", children: [{ id: "DDD", name: "DDD" }, { id: "EEE", name: "EEE" }] }, { id: "BBBB", name: "BBB", children: [{ id: "FFF", name: "FFF" }, { id: "GGG", name: "GGG", children: [{ id: "7777", name: "7777" }, { id: "8888", name: "8888" }] }] }] }] };
console.log(getItems(data, 'id', 'FFF'));
.as-console-wrapper { max-height: 100% !important; top: 0; }
You could implement a recursive search to find all paths and return the correct one when you reach the desired name-value pair.
const isObject = (obj) => obj === Object(obj);
let data = loadData();
let expected = [ '1111', '2222', 'BBB', 'FFF' ];
let actual = findPath(data, 'name', 'FFF');
console.log(JSON.stringify(expected) === JSON.stringify(actual));
function findPath(data, key, value, includeIndicies=false) {
let opts = { found : null, includeIndicies : includeIndicies };
findPathInternal(data, key, value, opts, []);
return opts.found;
}
function findPathInternal(node, key, val, opts, path) {
if (Array.isArray(node)) {
for (let i = 0; i < node.length; i++) {
findPathInternal(node[i], key, val, opts, opts.includeIndicies ? path.concat(i) : path);
}
} else if (isObject(node)) {
if (node[key] === val) {
opts.found = path.concat(val); return; // Exit
} else {
let keys = Object.keys(node);
for (let i = 0; i < keys.length; i++) {
findPathInternal(node[keys[i]], key, val, opts, path.concat(node[key]));
}
}
}
};
function loadData() {
return {
"id": "111",
"name": "1111",
"children": [{
"id": "22222",
"name": "2222",
"children": [{
"id": "AAAA",
"name": "AAAA",
"children": [{
"id": "DDD",
"name": "DDD"
},
{
"id": "EEE",
"name": "EEE"
}
]
},
{
"id": "BBBB",
"name": "BBB",
"children": [{
"id": "FFF",
"name": "FFF"
},
{
"id": "GGG",
"name": "GGG",
"children": [{
"id": "7777",
"name": "7777"
},
{
"id": "8888",
"name": "8888"
}
]
}
]
}
]
}]
};
}
.as-console-wrapper { top: 0; max-height: 100% !important; }
Related
I have an array of objects called employees. I need a solution that will return me a list of groups and respective employees present in the group along with the group properties.
The example is below, I have used an object but the result can also be an array that has a property called groupName within an object. [{groupName:"developer", employees:[],...}..] As long as the response returns a list of groups with their corresponding employees.
Below is the solution I did but I need a solution with a better time complexity that is O(n).
const employees = [
{ "name": "John Doe",
"id": "1",
"groups": [
{ "id": "developerId", "name": "developer", "color": "#fff" },
{ "id": "engineerId", "name": "engineer", "color": "#fff" }
],
"groupId":["developerId", "engineerId"]
},
{ "name": "Jane Doe",
"id": "2",
"groups": [
{ "id": "developerId", "name": "developer", "color": "#fff" },
{ "id": "testerId", "name": "tester", "color": "#fff" }
],
"groupId":["developerId", "testerId"]
}
]
//Solution O(m*n)
let groups = {};
employees.forEach((item) => {
item.groups.forEach((group) => {
if (!groups[group.name]) {
groups[group.name] = {
employees: [item.id],
...group,
};
} else {
groups[group.name].employees = [...groups[group.name].employees, item.id];
}
});
});
//result
{
"developer":{
"id":"developerId",
"employee":[
"1",
"2"
],
"color":"#fff"
},
"engineer":{
"id":"employeeId",
"employee":[
"1",
],
"color":"#fff"
},
"tester":{
"id":"testerId",
"employee":[
"2",
],
"color":"#fff"
}
}
Using Array#reduce and Array#forEach:
const employees = [
{
"name": "John Doe",
"id": "1",
"groups": [
{ "id": "developerId", "name": "developer", "color": "#fff" },
{ "id": "engineerId", "name": "engineer", "color": "#fff" }
],
"groupId": ["developerId", "engineerId"]
},
{
"name": "Jane Doe",
"id": "2",
"groups": [
{ "id": "developerId", "name": "developer", "color": "#fff" },
{ "id": "testerId", "name": "tester", "color": "#fff" }
],
"groupId": ["developerId", "testerId"]
}
];
const groups = employees.reduce((acc, { id: employeeId, groups = [] }) => {
groups.forEach(({ id, name, color }) => {
acc[name] = {
id, color, employee: [...(acc[name]?.employee ?? []), employeeId]
};
});
return acc;
}, {});
console.log(groups);
If you like to add some speed, you could use the old fashioned for statement for iterating, especially of having only a single result object.
This approach does not create an object again and again and uses the already existing objects.
const
employees = [{ name: "John Doe", id: "1", groups: [{ id: "developerId", name: "developer", color: "#fff" }, { id: "engineerId", name: "engineer", color: "#fff" }], groupId: ["developerId", "engineerId"] }, { name: "Jane Doe", id: "2", groups: [{ id: "developerId", name: "developer", color: "#fff" }, { id: "testerId", name: "tester", color: "#fff" }], groupId: ["developerId", "testerId"] }],
result = {};
for (const { id: employeeId, groups } of employees) {
for (const { id, name, color } of groups) {
result[name] ??= { id, color, employee: [] };
result[name].employee.push(employeeId);
}
}
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I have the following array of objects
{
"nome": "jose",
"categoria": [
{ "id": "1" },
{ "id": "3" },
]
},
{
"nome": "maria",
"categoria": [
{ "id": "2" },
]
},
{
"nome": "pedro",
"categoria": [
{ "id": "1" },
]
}
I have to reorder in another array of categories. Something like this:
{
"id": "1",
"pessoas": [
{
"nome": "jose",
"categoria": [
{ "id": "1" },
{ "id": "3" },
]
},
{
"nome": "pedro",
"categoria": [
{ "id": "1" },
]
},
]
},
{
"id": "2",
"pessoas": [
{
"nome": "maria",
"categoria": [
{ "id": "2" }
]
},
]
},
I have try with the function reduce(), but I couldn't because it is not an object, but a array of objects (categoria)
const group = data.reduce((r, array) => {
r[array.categoria.id] = [...r[array.categoria.id] || [], array];
return r;
}, {});
Someone can help me please?
You could take an object for grouping by id. Inside of reduce, categoria is iterated as well for getting the needed id.
var data = [{ nome: "jose", categoria: [{ id: "1" }, { id: "3" }] }, { nome: "maria", categoria: [{ id: "2" }] }, { nome: "pedro", categoria: [{ id: "1" }] }],
result = Object.values(data.reduce((r, o) => {
o.categoria.forEach(({ id }) => {
if (!r[id]) r[id] = { id, pessoas: [] };
r[id].pessoas.push(o);
});
return r;
}, {}));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I have this structure below, and I want to loop through the hierarchy without missing any object.
{
"countries": [
{
"name": "Denmark",
"id": "APA1",
"children": [
{
"name": "Zealand",
"id": "APA1.1",
"parentId": "APA1",
"children": [
{
"name": "Copenhagen",
"id": "APA1.1.1",
"parentId": "APA1.1",
"children": [
{
"name": "Dublin",
"id": "ANA1",
"parentId": "APA1.1.1.1",
"hostNames": [
{
"ip": "20.190.129.1"
},
{
"ip": "20.190.129.2"
}
]
}
]
}
]
},
{
"name": "Jutland",
"id": "APA1.2",
"parentId": "APA1",
"children": [
{
"name": "Nordjylland",
"id": "APA1.2.1",
"parentId": "APA1.2",
"children": [
{
"name": "Aalborg",
"id": "APA1.2.1.1",
"parentId": "APA1.2.1",
"children": [
{
"name": "Risskov",
"id": "ANA3",
"parentId": "APA1.2.1.1",
"hostNames": [
{
"ip": "40.101.81.146"
}
]
},
{
"name": "Brabrand",
"id": "ANA4",
"parentId": "APA1.2.1.1",
"hostNames": [
{
"ip": "43.203.94.182"
}
]
}
]
}
]
}
]
}
]
}
]
}
The reason why I want to loop through the hierarchy is that I want to turn this into a flat structure. So essentially I'm gonna take every object and move it to another array which has the structure that I want. I just want to know how to access the children.
The wanted result:
"applicationGroups": [
{
"id" : "APA1",
"name": "Denmark",
},
{
"name": "Zealand",
"id": "APA1.1",
"parentId": "APA1"
},
{
"name": "Copenhagen",
"id": "APA1.1.1",
"parentId": "APA1.1"
},
{
"name": "Dublin",
"id": "ANA1",
"parentId": "APA1.1.1.1"
},
{
"name": "Jutland",
"id": "APA1.2",
"parentId": "APA1"
},
{
"name": "Nordjylland",
"id": "APA1.2.1",
"parentId": "APA1.2"
},
{
"name": "Aalborg",
"id": "APA1.2.1.1",
"parentId": "APA1.2.1"
},
{
"name": "Risskov",
"id": "ANA3",
"parentId": "APA1.2.1.1"
},
{
"name": "Brabrand",
"id": "ANA4",
"parentId": "APA1.2.1.1"
}
]
I'm a bit new to JavaScript, and I don't really know where to start, but this example that I have given is not identical to the actual one that I'm working on, so I just want the principle so I can implement it myself in my actual code.
You could take a flatMap approach for the recursive call of a flattening callback.
const
flat = ({ hostNames, children = [], ...o }) => [o, ...children.flatMap(flat)],
data = { countries: [{ name: "Denmark", id: "APA1", children: [{ name: "Zealand", id: "APA1.1", parentId: "APA1", children: [{ name: "Copenhagen", id: "APA1.1.1", parentId: "APA1.1", children: [{ name: "Dublin", id: "ANA1", parentId: "APA1.1.1.1", hostNames: [{ ip: "20.190.129.1" }, { ip: "20.190.129.2" }] }] }] }, { name: "Jutland", id: "APA1.2", parentId: "APA1", children: [{ name: "Nordjylland", id: "APA1.2.1", parentId: "APA1.2", children: [{ name: "Aalborg", id: "APA1.2.1.1", parentId: "APA1.2.1", children: [{ name: "Risskov", id: "ANA3", parentId: "APA1.2.1.1", hostNames: [{ ip: "40.101.81.146" }] }, { name: "Brabrand", id: "ANA4", parentId: "APA1.2.1.1", hostNames: [{ ip: "43.203.94.182" }] }] }] }] }] }] },
result = data.countries.flatMap(flat);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can combine the Array.flat() method and this answer to flatten objects recursively.
Using recursive functions is the faster way to accomplish that.
To get a flat structure you could use reduce method to create recursive function.
const data = {"countries":[{"name":"Denmark","id":"APA1","children":[{"name":"Zealand","id":"APA1.1","parentId":"APA1","children":[{"name":"Copenhagen","id":"APA1.1.1","parentId":"APA1.1","children":[{"name":"Dublin","id":"ANA1","parentId":"APA1.1.1.1","hostNames":[{"ip":"20.190.129.1"},{"ip":"20.190.129.2"}]}]}]},{"name":"Jutland","id":"APA1.2","parentId":"APA1","children":[{"name":"Nordjylland","id":"APA1.2.1","parentId":"APA1.2","children":[{"name":"Aalborg","id":"APA1.2.1.1","parentId":"APA1.2.1","children":[{"name":"Risskov","id":"ANA3","parentId":"APA1.2.1.1","hostNames":[{"ip":"40.101.81.146"}]},{"name":"Brabrand","id":"ANA4","parentId":"APA1.2.1.1","hostNames":[{"ip":"43.203.94.182"}]}]}]}]}]}]}
function flat(data) {
return data.reduce((r, { children, ...rest }) => {
if (children) r.push(...flat(children))
r.push(rest)
return r;
}, [])
}
const result = flat(data.countries)
console.log(result)
Below is the json I have:
{
"name": "test",
"characteristics": [
{
"name": "A",
"description": "",
"comprisedOf": [
{
"name": "A1",
"description": "",
"comprisedOf": [
{
"name": "A1.1",
"description": "",
"value": "10"
},
{
"name": "A1.2",
"description": "",
"value": "5"
}
]
},
{
"name": "A2",
"description": "",
"value": "100"
},
{
"name": "A3",
"description": "",
"value": "20"
},
{
"name": "A4",
"description": "",
"value": "50"
}
]
},
{
"name": "B",
"description": "",
"value": "10"
}
]
}
Here I have 2 characteristics object of "test" - A and B.These 2 objects may or may not have "comprisedOf" array. For Eg A has"comprisedOf" array whereas B has not. These comprisedOf array further may or may not have comprisedof objects array.
Now i have to make an array out of this which should be like the given below array:-
[
{
"name": "test",
"children": [
{
"name": "A",
"children": [
{
"name":"A1",
"children:: [
{
"name": "A1.1"
},
{
"name": "A1.2"
}
]
},
{
"name": "A2"
},
{
"name": "A3"
},
{
"name": "A4"
}
]
},
{
"name": "B"
}
]
}
]
How this array can be formed recursively out of the first array?
Thanks so much!!
EDIT:- Here's what I have tried:-
var makeChildrenAll = function(dataChar){
dataChar.forEach(function(char){
if(char.comprisedOf) {
makeChildrenAll(char.comprisedOf);
}
childrenData.push({
name: char.name,
description: char.description,
children: childrenData
});
});
};
makeChildrenAll(dataChar);
In order to be recursive you want the function to return and be able to assign that return to current object
Something like
function mapItems(arr){
// return new array
return arr.reduce((a,c)=>{
var newObj={name:c.name};
if(Array.isArray(c.comprisedOf)){
// use returned array of recursive function to assign to `children` property
newObj.children = mapItems(c.comprisedOf)
}
return a.concat(newObj)
},[])
}
console.log(mapItems(data.characteristics))
.as-console-wrapper { max-height: 100%!important;}
<script>
var data ={
"name": "test",
"characteristics": [
{
"name": "A",
"description": "",
"comprisedOf": [
{
"name": "A1",
"description": "",
"comprisedOf": [
{
"name": "A1.1",
"description": "",
"value": "10"
},
{
"name": "A1.2",
"description": "",
"value": "5"
}
]
},
{
"name": "A2",
"description": "",
"value": "100"
},
{
"name": "A3",
"description": "",
"value": "20"
},
{
"name": "A4",
"description": "",
"value": "50"
}
]
},
{
"name": "B",
"description": "",
"value": "10"
}
]
}
</script>
You could take a different start array and use a destructuring for the wanted properties.
function mapItems(array) {
return array.map(({ name, comprisedOf }) =>
Object.assign({ name }, comprisedOf && { children: mapItems(comprisedOf) }));
}
var data = { name: "test", characteristics: [{ name: "A", description: "", comprisedOf: [{ name: "A1", description: "", comprisedOf: [{ name: "A1.1", description: "", value: "10" }, { name: "A1.2", description: "", value: "5" }] }, { name: "A2", description: "", value: "100" }, { name: "A3", description: "", value: "20" }, { name: "A4", description: "", value: "50" }] }, { name: "B", description: "", value: "10" }] };
console.log(mapItems([{ name: data.name, comprisedOf: data.characteristics }]));
.as-console-wrapper { max-height: 100% !important; top: 0; }
ES5
function mapItems(array) {
return array.map(function (o) {
var r = { name: o.name };
if (o.comprisedOf) {
r.children = mapItems(o.comprisedOf);
}
return r;
});
}
var data = { name: "test", characteristics: [{ name: "A", description: "", comprisedOf: [{ name: "A1", description: "", comprisedOf: [{ name: "A1.1", description: "", value: "10" }, { name: "A1.2", description: "", value: "5" }] }, { name: "A2", description: "", value: "100" }, { name: "A3", description: "", value: "20" }, { name: "A4", description: "", value: "50" }] }, { name: "B", description: "", value: "10" }] };
console.log(mapItems([{ name: data.name, comprisedOf: data.characteristics }]));
.as-console-wrapper { max-height: 100% !important; top: 0; }
I have below dynamic nested JSON object arrays and I wanted to get the desired output with JavaScript grouped by id from both.
First Array:
[
{
"id": "11",
"name": "emp1",
"location": [
{ "name": "abc", "id": "lc1" }
]
},
{
"id": "11",
"name": "emp2",
"location": [
{ "name": "abc", "id": "lc1" },
]
},
{
"id": "22",
"name": "emp3",
"location": [
{ "name": "xyz", "id": "lc2" }
]
}
]
Second array like below.
[
{
"name": "sub1",
"id": "11"
...
},
{
"name": "sub1.1",
"id": "11"
...
},
{
"name": "sub2",
"id": "22"
...
}
]
Desired Output:
[
{
"id": "11",
"first": [{"name": "emp1"},
{"name": "emp2"}],
"second": [{"name": "sub1"},{"name": "sub1.1"}],
"location": [{"name": "abc"}]
},
{
"id": "22",
"first": [{"name": "emp3"}],
"second": [{"name": "sub2"}],
"location": [{"name": "xyz"}]
}
]
How to get the desired output like above using javascript/angularjs?
I would do it using the amazing Array#reduce function.
Note that I have named your first array as a1, second as a2 and result as res.
a1.reduce(function(arr, obj) {
var existing = arr.filter(function(res) {
return res.id === obj.id
})[0]
if (existing) {
existing.first.push({
name: obj.name
})
} else {
var second = a2.filter(function(res) {
return res.id === obj.id
})
var secondObj = second.length ? second.map(function(sec) {
return {
name: sec.name
};
}) : []
arr.push({
id: obj.id,
first: [{
name: obj.name
}],
second: secondObj,
location: obj.location
})
}
return arr;
}, [])
Here's the working snippet. Take a look!
var a1 = [{
"id": "11",
"name": "emp1",
"location": [{
"name": "abc",
"id": "lc1"
}]
},
{
"id": "11",
"name": "emp2",
"location": [{
"name": "abc",
"id": "lc1"
}]
},
{
"id": "22",
"name": "emp3",
"location": [{
"name": "xyz",
"id": "lc2"
}]
}
]
var a2 = [{
"name": "sub1",
"id": "11"
}, {
"name": "sub1.1",
"id": "11"
},
{
"name": "sub2",
"id": "22"
}
]
var res = a1.reduce(function(arr, obj) {
var existing = arr.filter(function(res) {
return res.id === obj.id
})[0]
if (existing) {
existing.first.push({
name: obj.name
})
} else {
var second = a2.filter(function(res) {
return res.id === obj.id
})
var secondObj = second.length ? second.map(function(sec) {
return {
name: sec.name
};
}) : []
arr.push({
id: obj.id,
first: [{
name: obj.name
}],
second: secondObj,
location: obj.location
})
}
return arr;
}, [])
console.log(res)
.as-console-wrapper {
max-height: 100% !important;
top: 0;
}
var red1 = [{
"id": "11",
"name": "emp1",
"location": [{
"name": "abc",
"id": "lc1"
}]
},
{
"id": "11",
"name": "emp2",
"location": [{
"name": "abc",
"id": "lc1"
}]
},
{
"id": "22",
"name": "emp3",
"location": [{
"name": "xyz",
"id": "lc2"
}]
}
]
var b = [{
"name": "sub1",
"id": "11"
},
{
"name": "sub2",
"id": "22"
}
]
var identication = {}
var result = []
red1.forEach(function(val) {
if (val['id'] in identication) {
var t = {}
t['name'] = val['name']
result[identication[val['id']]]['first'].push(t)
} else {
var t = {}
t['name'] = val['name']
val['first'] = []
val['first'].push(t)
delete val['name']
var identity = result.push(val)
identication[val['id']] = identity - 1;
}
})
b.forEach(function(d) {
if (d['id'] in identication) {
var t = {
'name': d['name']
}
if (!('second' in result[identication[d['id']]])) {
result[identication[d['id']]]['second'] = []
}
result[identication[d['id']]]['second'].push(t)
} else {
var t = {}
for (key in d) {
if (key == 'name')
continue
t[key] = d[key]
}
t['second'] = [{
'name': d['name']
}]
var identity = result.push(t)
identication[d['id']] = identity - 1;
}
})
console.log(result)