This is the flat object I'm working with, it has many more results, ~6800. I've been trying to convert it to a nested tree (like the one listed below) for about 13 hours now & I'm truly lost.
[
{
"make": "Acura",
"classification": "Mid SUV",
"segment": "Competitive Trucks",
"model": "RDX",
"catalogDetail": "RDX_SUV_4_Gasoline_2013_Base w/Tech_FWD_3.5_6_105.7_Automatic"
},
{
"make": "Acura",
"classification": "Midsize Car",
"segment": "Competitive Cars",
"model": "TSX",
"catalogDetail": "TSX_Sedan_4_Gasoline_2012_Base w/Tech_FWD_2.4_4_106.4_Automatic"
},
{
"make": "Aston Martin",
"classification": "Compact Car",
"segment": "Competitive Cars",
"model": "DB11",
"catalogDetail": "DB11_Convertible_2_Gasoline_2019_Volante_RWD_4.0_8_110.4_Automatic"
}
]
What I'm trying to do is build this flat object into a nested structure like this:
[
{
"make": [
{ "Acura",
"classification": [{
"Mid SUV",
"segment": [{
"Competitive Trucks",
"model": [{
"RDX",
"catalogDetail": [{
"RDX_SUV_4_Gasoline_2013_Base w/Tech_FWD_3.5_6_105.7_Automatic"
}]
}]
}],
"Midsize Car",
"segment": [{
"Competitive Cars",
"model": [{
"TSX",
"catalogDetail": [{
"TSX_Sedan_4_Gasoline_2012_Base w/Tech_FWD_2.4_4_106.4_Automatic"
}]
}]
}]
}],
}
]
},
{
"make": [
{ "Aston Martin",
"classification": [{
"Compact Car",
"segment": [{
"Competitive Cars",
"model": [{
"DB11",
"catalogDetail": [{
"DB11_Convertible_2_Gasoline_2019_Volante_RWD_4.0_8_110.4_Automatic"
}]
}]
}]
}]
}
]
}
]
Where the structure falls into a nested structure like: make --> classification --> segment --> model --> catalogdetail. So there would be multiple car makes, ford, Cadillac, etc. Multiple classifications, multiple different segments under each make.
This is what I've tried:
this._servicesService.getHierarchy().subscribe(data => {
console.log(data)
/* this.data = data;*/
/* this.dataStore = data;*/
let distinctSeg = [...new Set(data.map(x => x.segment))];
let distinctClass = [...new Set(data.map(x => x.classification))];
let distinctMod = [...new Set(data.map(x => x.model))];
let distinctCd = [...new Set(data.map(x => x.catalogDetail))];
const newData = [];
data.forEach(e => {
if (newData.length == 0) {
newData.push({
make: e.make,
segment: e.segment,
classification: e.classification,
model: [e.model],
catalogDetail: [e.catalogDetail]
});
} else {
let foundIndex = newData.findIndex(fi => fi.make === e.make, fi => fi.segment = e.segment);
if (foundIndex >= 0) {
/* newData[foundIndex].make.push(e.make),*/
/* newData[foundIndex].segment.push(e.segment),*/
/* newData[foundIndex].classification.push(e.classification),*/
newData[foundIndex].model.push(e.model);
newData[foundIndex].catalogDetail.push(e.catalogDetail);
} else {
newData.push({
make: e.make,
segment: distinctSeg,
classification: distinctClass,
model: [e.model],
catalogDetail: [e.catalogDetail]
});
}
}
});
console.log(newData);
})
This give me distinct values for model, segment and class, (not model or catalogDetail for some reason) but the nested structure isn't there & i'm truly lost on how to proceed. I've looked at a number of examples on here & I really haven't been successful applying any of the previously listed routes. Any insight or tips would be greatly appreciated. I attached a picture to better visualize the final desired output in case I have the wrong syntax. tree
CodePudding user response:
I recommend you to use the Array.reduce method for this.
This method allows you to easily convert a js array to an object.
You can do something like the following:
arr.reduce((accumulator, currentValue)=>{
if(!accumulator[currentValue.make]){
accumulator[currentValue.make] = []
}
accumulator[currentValue.make][0] = {...<PUT YOUR OBJECT VALUE HERE>}
},{})
CodePudding user response:
As Avrham say, you can use reduce. Imagine you create a function like
reduce(data:any[],key:string,keyArray:string)
{
return data.reduce((a:any[],b:any)=>{
const element=a.find(x=>x[key]==b[key])
const value=b[key]
delete b[key]
if (!element)
a.push({[key]:value,[keyArray]:[b]})
else
element[keyArray].push(b)
return a;
},[])
}
}
You can use in the way:
const data=this.reduce(this.data,"make","classification")
data.forEach(x=>{
x.classification=this.reduce(x.classification,"classification","segment")
x.classification.forEach(c=>{
c.segment=this.reduce(c.segment,"segment","model")
c.segment.forEach(m=>{
m.model=this.reduce(m.model,"model","catalogDetail")
m.model.forEach(d=>d.catalogDetail=d.catalogDetail.map(e=>e.catalogDetail))
})
})
})
this.dataFormatted=data
But I think that it's not easy mannage this specie of tree-view. In general you has a tree-view and all the arrays are "children". So I suggest create a function
reduceChildren(data:any[],key:string)
{
return data.reduce((a:any[],b:any)=>{
const element=a.find(x=>x[key]==b[key])
const value=b[key]
delete b[key]
if (!element)
a.push({[key]:value,children:[b]})
else
element.children.push(b)
return a;
},[])
}
You can use like
const data=this.reduceChildren(this.data,"make")
data.forEach(x=>{
x.children=this.reduceChildren(x.children,"classification")
x.children.forEach(c=>{
c.children=this.reduceChildren(c.children,"segment")
c.children.forEach(m=>{
m.children=this.reduceChildren(m.children,"model")
m.children.forEach(d=>d.children=d.children.map(e=>e.catalogDetail))
})
})
})
this.dataFormatted=data
You can see the two approach in this stackblitz