Can anyone help converting the following list of parent-child objects:
I have below array of objects, Need to convert it into parent child order. each 'Members' attribute in object may have 1 or n objects inside it. In 'Members' array the 1st object is parent of 2nd one and 2nd is parent of third object.
So in 1st Member
'Video' is parent of 'West' and 'West' is parent of 'India' and so on..
I have tried to loop through the elements one by one but could not reach the desired outcome.
Any help with the logic or code would be really helpful.
Input :
[
{
"Members": [
{
"Name": "Videos"
},
{
"Name": "West"
},
{
"Name": "India"
}
]
},
{
"Members": [
{
"Name": "Machinery"
},
{
"Name": "South"
},
{
"Name": "Australia"
}
]
},
{
"Members": [
{
"Name": "Electronics"
},
{
"Name": "Midwest"
},
{
"Name": "Arab"
}
]
},
{
"Members": [
{
"Name": "Machinery"
},
{
"Name": "West"
},
{
"Name": "India"
}
]
},
{
"Members": [
{
"Name": "Electronics"
},
{
"Name": "NorthEast"
},
{
"Name": "Japan"
}
]
},
{
"Members": [
{
"Name": "Videos"
},
{
"Name": "South"
},
{
"Name": "Australia"
}
]
},
{
"Members": [
{
"Name": "Videos"
},
{
"Name": "West"
},
{
"Name": "Japan"
}
]
}
]
Expected Output :
[
{
"name": "Videos",
"children": [
{
"name": "West",
"children": [
{
"name": "India",
"children": []
},
{
"name": "Japan",
"children": []
}
]
},
{
"name": "South",
"children": [
{
"name": "Australia",
"children": []
}
]
}
]
},
{
"name": "Machinery",
"children": [
{
"name": "South",
"children": [
{
"name": "Australia",
"children": []
}
]
},
{
"name": "West",
"children": [
{
"name": "India",
"children": []
}
]
}
]
},
{
"name": "Electronics",
"children": [
{
"name": "Midwest",
"children": [
{
"name": "Arab",
"children": []
}
]
},
{
"name": "NorthEast",
"children": [
{
"name": "Japan",
"children": []
}
]
}
]
}
]
```
Man this took too long. But it works with larger datasets. Note to OP, never use this data structure. Ever. It's horrible. I lost many braincells making this solution:
var arr = [
{Members: [{ Name: "Videos" }, { Name: "West" }, { Name: "India" }, {Name: 'Testing'}]},
{Members: [{ Name: "Machinery" }, { Name: "South" }, { Name: "Australia" }]},
{Members: [{ Name: "Electronics" }, { Name: "Midwest" }, { Name: "Arab" }]},
{Members: [{ Name: "Machinery" }, { Name: "West" }, { Name: "India" }]},
{Members: [{ Name: "Electronics" }, { Name: "NorthEast" }, { Name: "Japan" }]},
{Members: [{ Name: "Videos" }, { Name: "South" }, { Name: "Australia" }]},
{Members: [{ Name: "Videos" }, { Name: "West" }, { Name: "Japan" }]}
];
const addRelation = (obj, m, i) => ({...obj, parent: i === 0 ? null : m.slice(0, i).map(el => el.Name).join('.'), level: i, children: []})
const arrayToTree = (arr) => {
arr = arr.map(({ Members: m }) => m.map((obj, i) => addRelation(obj, m, i))).reduce((acc, arr) => {
arr.map(obj => acc.push(obj))
return acc
}, []).sort((a, b) => b.level - a.level)
var temp = [...arr].filter((o, index, self) =>
index === self.findIndex((t) => (
t.Name === o.Name && t.parent === o.parent
))
)
arr.forEach(() => {
if (temp[0].level === 0) return
var parentIndex = temp.findIndex(o => {
var str = temp[0].parent
var rest = str.substring(0, str.lastIndexOf("."));
var last = str.substring(str.lastIndexOf(".") + 1, str.length);
var parents = [rest, last]
return parents[0] !== ''
? o.Name === parents[1] && o.parent === parents[0]
: o.Name === temp[0].parent
})
const { Name, children } = temp[0]
temp[parentIndex].children.push({Name, children})
temp.shift()
})
return temp.map(({ Name, children }) => ({ Name, children }))
}
arr = arrayToTree(arr)
console.log(arr)
This might not be the best way to go about this and this only works for 3 levels.
var data = [
{
"Members": [
{
"Name": "Videos"
},
{
"Name": "West"
},
{
"Name": "India"
}
]
},
{
"Members": [
{
"Name": "Machinery"
},
{
"Name": "South"
},
{
"Name": "Australia"
}
]
},
{
"Members": [
{
"Name": "Electronics"
},
{
"Name": "Midwest"
},
{
"Name": "Arab"
}
]
},
{
"Members": [
{
"Name": "Machinery"
},
{
"Name": "West"
},
{
"Name": "India"
}
]
},
{
"Members": [
{
"Name": "Electronics"
},
{
"Name": "NorthEast"
},
{
"Name": "Japan"
}
]
},
{
"Members": [
{
"Name": "Videos"
},
{
"Name": "South"
},
{
"Name": "Australia"
}
]
},
{
"Members": [
{
"Name": "Videos"
},
{
"Name": "West"
},
{
"Name": "Japan"
}
]
}
];
function organize(dataBefore){
var dataAfter = [];
dataAfter.push({
name: dataBefore[0].Members[0].Name,
children: []
});
for(var i = 1; i < dataBefore.length; i++){
if(!doesExist(dataAfter, dataBefore[i].Members[0].Name)){
dataAfter.push({
name: dataBefore[i].Members[0].Name,
children: []
});
}
}
dataAfter[0].children.push({
name: dataBefore[0].Members[1].Name,
children: []
});
for(var i = 1; i < dataBefore.length; i++){
for(var j = 0; j < dataAfter.length; j++){
if(dataAfter[j].name == dataBefore[i].Members[0].Name){
if(!doesExist(dataAfter[j].children, dataBefore[i].Members[1].Name)){
dataAfter[j].children.push({
name: dataBefore[i].Members[1].Name,
children: []
});
}
}
}
}
dataAfter[0].children[0].children.push({
name: dataBefore[0].Members[2].Name,
children: []
});
for(var i = 1; i < dataBefore.length; i++){
for(var j = 0; j < dataAfter.length; j++){
if(dataAfter[j].name == dataBefore[i].Members[0].Name){
for(var k = 0; k < dataAfter[j].children.length; k++){
if(dataAfter[j].children[k].name == dataBefore[i].Members[1].Name){
if(!doesExist(dataAfter[j].children[k].children, dataBefore[i].Members[2].Name)){
dataAfter[j].children[k].children.push({
name: dataBefore[i].Members[2].Name,
children: []
});
}
}
}
}
}
}
return dataAfter;
}
function doesExist(checkThisData, searchValue){
for(var i = 0; i < checkThisData.length; i++){
if(searchValue == checkThisData[i].name){
return true;
}
}
return false;
}
console.log(organize(data));
I would advise to use map + reduce combination:
const data = [{
"Members": [{
"Name": "Videos"
},
{
"Name": "West"
},
{
"Name": "India"
}
]
},
{
"Members": [{
"Name": "Machinery"
},
{
"Name": "South"
},
{
"Name": "Australia"
}
]
},
{
"Members": [{
"Name": "Electronics"
},
{
"Name": "Midwest"
},
{
"Name": "Arab"
}
]
},
{
"Members": [{
"Name": "Machinery"
},
{
"Name": "West"
},
{
"Name": "India"
}
]
},
{
"Members": [{
"Name": "Electronics"
},
{
"Name": "NorthEast"
},
{
"Name": "Japan"
}
]
},
{
"Members": [{
"Name": "Videos"
},
{
"Name": "South"
},
{
"Name": "Australia"
}
]
},
{
"Members": [{
"Name": "Videos"
},
{
"Name": "West"
},
{
"Name": "Japan"
}
]
}
];
const ar = data.map((el, i) => {
let a = el['Members'].reduce((acc, member, index) => {
if (index === 0) {
acc[index] = {
name: member.Name,
children: []
};
} else {
debugger;
if (acc[0].children.length === 0) {
acc[0].children.push({
name: member.Name,
children: []
});
} else {
acc[0].children[0].children.push({
name: member.Name,
children: []
});
}
}
return acc;
}, []);
return a;
});
console.log(ar);
Related
I'm trying to filter an array with nested objects like this:
const items = [
{
"id": 1,
"name": "test1",
"subitems": [
{
"id": 2,
"name": "test2",
"subsubitems": [
{
"id": 3,
"name": "test3",
},
{
"id": 4,
"name": "test4",
}
]
}
]
},
{
"id": 10,
"name": "test10",
"subitems": [
{
"id": 20,
"name": "test20",
"subsubitems": [
{
"id": 30,
"name": "test30",
}
]
}
]
}
]
const filteredResults = items.filter((item) =>
item.subitems.filter((subitem) =>
subitem.subsubitems.filter((subsubitem) =>
subsubitem.name.includes('test3')
)
)
)
console.log(filteredResults)
But it's not filtering correctly, the original array is being returned. Right now I'm just attempting to filter at the subsubitems level. Ultimately I want to return an array with any matches to name at any level.
So test2 would return an array like this:
[
{
"id": 1,
"name": "test1",
"subitems": [
{
"id": 2,
"name": "test2",
"subsubitems": [
{
"id": 3,
"name": "test3",
},
{
"id": 4,
"name": "test4",
}
]
}
]
}
]
And test3 would return an array like this:
[
{
"id": 1,
"name": "test1",
"subitems": [
{
"id": 2,
"name": "test2",
"subsubitems": [
{
"id": 3,
"name": "test3",
}
]
}
]
}
]
And test would return everything.
Try This:
const items = [
{
id: 1,
name: "test1",
subitems: [
{
id: 2,
name: "test2",
subsubitems: [
{
id: 3,
name: "test3",
},
{
id: 4,
name: "test4",
},
],
},
],
},
{
id: 10,
name: "test10",
subitems: [
{
id: 20,
name: "test20",
subsubitems: [
{
id: 30,
name: "test30",
},
],
},
],
},
];
const filterItems = (items, filter) => {
const result = [];
items.forEach((item) => {
if (item.name === filter) {
result.push(item);
}
item.subitems.forEach((subitem) => {
if (subitem.name === filter) {
result.push(item);
}
subitem.subsubitems.forEach((subsubitem) => {
if (subsubitem.name === filter) {
result.push(item);
}
});
});
});
console.log("filtered", result);
};
filterItems(items, "test10");
Result is :
filtered [ { id: 10, name: 'test10', subitems: [ [Object] ] } ]
Input:
[{
"dimensions": [{
"name": "Size",
"value": "Size1"
},
{
"name": "Color",
"value": "Color1"
},
{
"name": "Pattern",
"value": "1"
}
],
"link": "link",
"title": "title"
},
{
"dimensions": [{
"name": "Size",
"value": "Size2"
},
{
"name": "Color",
"value": "Color1"
},
{
"name": "Pattern",
"value": "2"
}
],
"link": "link1",
"title": "title"
},
{
"dimensions": [{
"name": "Size",
"value": "Size1"
},
{
"name": "Color",
"value": "Color2"
},
{
"name": "Pattern",
"value": "1"
}
],
"link": "link",
"title": "title"
},
{
"dimensions": [{
"name": "Size",
"value": "Size2"
},
{
"name": "Color",
"value": "Color1"
},
{
"name": "Pattern",
"value": "3"
}
],
"link": "link",
"title": "title"
}]
Expected Output:
{
"levels":3,
"level_1_name":"Size",
"level_2_name":"Color",
"level_3_name":"Pattern",
"data":[
{
"value":"Size1",
"data":[
{
"value":"Color1",
"data":[
{
"value":"1"
}
]
}, {
"value":"Color2",
"data":[
{
"value":"4"
}
]
}
]
}, {
"value":"Size2",
"data":[
{
"value":"Color1",
"data":[
{
"value":"3"
}
]
},
{
"value":"Color2",
"data":[
{
"value":"2"
}
]
}
]
}
]
}
I've tried something like that
for(index=0; index<data[0].dimensions.length - 1; index++) {
let temp = _(data).groupBy(function(o) {
return o.dimensions[index].value
})
let keys = Object.keys(temp)
addData(final, keys, temp)
}
obj["data"] = final
function addData(data, keys, temp) {
if (data && data.length) {
return data.forEach(function(data1){
console.log(data1)
return addData(data1, keys, temp)
})
} else {
let data_arr = []
if (Array.isArray(data)) {
keys.forEach(function(key) {
data.push({
value: key,
data: temp[key]
})
})
} else {
keys.forEach(function(key) {
let data_obj = {}
data_obj['value'] = key
data_obj['data'] = temp[key]
data_arr.push(data_obj)
})
data["data"] = data_arr
}
}
}
I've tried the logic to format as per the expected output. It works with level 2 input set, But the logic written doesn't work for level 3 input data set.
Also It would be helpful if you can suggest any algorithms to sort this problem out.
Thanks in advance!
Here is a fairly compact solution using reduce(). (I've edited the input to match your expected output.)
const source = [{
"dimensions": [{
"name": "Size",
"value": "Size1"
},
{
"name": "Color",
"value": "Color1"
},
{
"name": "Pattern",
"value": "1"
}
],
"link": "link",
"title": "title"
},
{
"dimensions": [{
"name": "Size",
"value": "Size2"
},
{
"name": "Color",
"value": "Color2"
},
{
"name": "Pattern",
"value": "2"
}
],
"link": "link1",
"title": "title"
},
{
"dimensions": [{
"name": "Size",
"value": "Size1"
},
{
"name": "Color",
"value": "Color2"
},
{
"name": "Pattern",
"value": "4"
}
],
"link": "link",
"title": "title"
},
{
"dimensions": [{
"name": "Size",
"value": "Size2"
},
{
"name": "Color",
"value": "Color1"
},
{
"name": "Pattern",
"value": "3"
}
],
"link": "link",
"title": "title"
}];
const output = source.reduce((acc, {dimensions: dims}) => {
const levels = dims.length;
// initialize top-level keys based on first object
if (!acc.hasOwnProperty('levels')) {
acc.levels = levels;
dims.forEach((level, i) => acc[`level_${i+1}_name`] = level.name);
acc.data = [];
}
// iterate over dimension objects and merge with accumulator
let parent = acc.data;
dims.forEach((o, i) => {
let lvlObj = parent.find(e => e.value === o.value);
if (!lvlObj) {
lvlObj = i < levels - 1 ?
{value: o.value, data: []} :
{value: o.value};
parent.push({...lvlObj});
}
parent = lvlObj.data;
});
return acc;
}, {});
console.log(output);
In vanilla JavaScript, how would I find unique locations from this object and make them keys, and place all items with that location as values. (can install lodash if necessary).
So this:
[
{
"item": {
"id": "cat"
},
"location": {
"id": "porch"
}
},
{
"item": {
"id": "dog"
},
"location": {
"id": "porch"
}
},
{
"item": {
"id": "snake"
},
"location": {
"id": "forest"
}
},
{
"item": {
"id": "bird"
},
"location": {
"id": "forest"
}
},
{
"item": {
"id": "beer"
},
"location": {
"id": "fridge"
}
}
]
Becomes this:
[
{
"porch": [
{
"id": "cat"
},
{
"id": "dog"
}
]
},
{
"forest": [
{
"id": "snake"
},
{
"id": "bird"
}
]
},
{
"fridge": [
{
"id": "beer"
}
]
}
]
PEN
// modified desired result
[
{
"location": {
"name": "porch",
"items": [
{
"title": "cat"
},
{
"title": "dog"
}
]
}
},
{
"location": {
"name": "forest",
"items": [
{
"title": "snake"
},
{
"title": "bird"
}
]
}
},
{
"location": {
"name": "fridge",
"items": [
{
"title": "beer"
}
]
}
}
]
let obj = [
{
"item": {
"id": "cat"
},
"location": {
"id": "porch"
}
},
{
"item": {
"id": "dog"
},
"location": {
"id": "porch"
}
},
{
"item": {
"id": "snake"
},
"location": {
"id": "forest"
}
},
{
"item": {
"id": "bird"
},
"location": {
"id": "forest"
}
},
{
"item": {
"id": "beer"
},
"location": {
"id": "fridge"
}
}
]
let result = {};
obj.forEach(({item, location}) => {
if(!result[location.id]) result[location.id] = []
result[location.id].push({title: item.id})
})
result = Object.keys(result).map(key => ({
"location": {
"name": key,
"items": result[key]
}
}))
result contains required output.
I have this structure of JS object:
const obj1 = {
"groups": [
{
"name": "test",
"type": "app"
},
{
"name": "test2",
"type": "app"
},
{
"name": "test1",
"type": "app2"
},
{
"name": "test3",
"type": "app2"
}
]
}
I need to finaly get this:
Result:
const obj1 = {
app: {groups: [{name: 'test'},{name: 'test2'}]},
app2: {groups: [{name: 'test1'},{name: 'test3'}]},
}
May be ES6+ aproach or older
This comes down to just iterating through obj1.groups building up a new output object:
const obj1 = {
"groups": [
{ "name": "test", "type": "app" },
{ "name": "test2", "type": "app" },
{ "name": "test1", "type": "app2" },
{ "name": "test3", "type": "app2" }
]
};
const out = {};
for (let group of obj1.groups) {
out[group.type] = out[group.type] || { groups: [] };
out[group.type].groups.push({ name: group.name });
}
console.log(out);
Alternatively, you could use Array.reduce() to wrap this up into a single expression:
const obj1 = {
"groups": [
{ "name": "test", "type": "app" },
{ "name": "test2", "type": "app" },
{ "name": "test1", "type": "app2" },
{ "name": "test3", "type": "app2" }
]
};
const out = obj1.groups.reduce((out, group) => {
out[group.type] = out[group.type] || { groups: [] };
out[group.type].groups.push({ name: group.name });
return out;
}, {});
console.log(out);
You can do it succinctly using Array.reduce():
const obj1 = {
"groups": [{ "name": "test", "type": "app" }, { "name": "test2", "type": "app" }, { "name": "test1", "type": "app2" }, { "name": "test3", "type": "app2" }]
};
const result = obj1.groups.reduce((obj, { name, type }) => {
return (obj[type].groups.push({ name }), obj);
}, { app: { groups: [] }, app2: { groups: [] } });
console.log(result);
Or using the spread operator:
const obj1 = {
"groups": [{ "name": "test", "type": "app" }, { "name": "test2", "type": "app" }, { "name": "test1", "type": "app2" }, { "name": "test3", "type": "app2" }]
};
const result = obj1.groups.reduce((o, { name, type }) =>
({ ...o, [type]: { groups: [...((o[type] || {}).groups || []), { name }] } }), {});
console.log(result);
I have the json Like below, i want to get the unique child sub nodes for each main nodes
{
"name": "MENUS",
"value": "",
"children": [
{
"name": "MENU1",
"value": {},
"children": [
{
"name": "SubMenu1",
"value": {},
"children": [
{
"name": "SubMenu2",
"value": {},
"children": [
{
"name": "SubMenu3",
"value": {
"Pld": "1"
},
"children": []
}
]
}
]
}
]
},
{
"name": "MENU1",
"value": {},
"children": [
{
"name": "SubMenu1",
"value": {},
"children": [
{
"name": "SubMenu2",
"value": {},
"children": [
{
"name": "SubMenu4",
"value": {
"Pld": "1"
},
"children": []
}
]
}
]
}
]
}
]
}
I wanted the JSON like below format for each main nodes i want unique sub nodes means the subnode should not be a duplicate in the JSON.
{
"name": "MENUS",
"value": "",
"children": [
{
"name": "MENU1",
"value": {},
"children": [
{
"name": "SubMenu1",
"value": {},
"children": [
{
"name": "SubMenu2",
"value": {},
"children": [
{
"name": "SubMenu3",
"value": {
"Pld": "1"
},
"children": []
},
{
"name": "SubMenu4",
"value": {
"Pld": "1"
},
"children": []
}
]
}
]
}
]
}
]
}
Could you please anyone help me how to get the unique hierarchical tree JSON.
EDIT::Adding POJO classes for reference
This is how my java pojo looks like,
MenuTree.java
private String name;
private String value;
private Children[] children;
Children.java
private String name;
private Value value;
private Children[] children;
Value.java
private String Pld;
You could use an iterative and recursive approach by searching eachlevel for existing name and append the array with new objects if not found.
var object = { name: "MENUS", value: "", children: [{ name: "MENU1", value: {}, children: [{ name: "SubMenu1", value: {}, children: [{ name: "SubMenu2", value: {}, children: [{ name: "SubMenu3", value: { Pld: "1" }, children: [] }] }] }] }, { name: "MENU1", value: {}, children: [{ name: "SubMenu1", value: {}, children: [{ name: "SubMenu2", value: {}, children: [{ name: "SubMenu4", value: { Pld: "1" }, children: [] }] }] }] }] },
unique = [object].reduce(function iter(r, { name, value, children = [] }) {
var object = r.find(o => o.name === name);
if (!object) {
object = { name, value };
r.push(object);
}
children.reduce(iter, object.children = object.children || []);
return r;
}, []);
console.log(unique);
.as-console-wrapper { max-height: 100% !important; top: 0; }