Home > Blockchain >  Typescript Flat Object to Nested Tree Object, How to build it with distinct values?
Typescript Flat Object to Nested Tree Object, How to build it with distinct values?

Time:11-21

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

  • Related