Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Build JSON from JavaScript array recursively

I want to build a JSON object from an array returned from http request my actual array look like:

[{
    Description: "Product"
    IsInstanciable: false
    IsMasked: false
    Name: "ProductBase"
    Subclasses: [{
      Description: "Product2"
      IsInstanciable: false
      IsMasked: false
      Name: "ProductBase2",
      Count: 5,
      Subclasses:[]
    },
    {
      Description: "Product3"
      IsInstanciable: false
      IsMasked: false
      Name: "ProductBase3",
      Count: 4,
      Subclasses:[],
    }]
},
{
    Description: "Product4"
    IsInstanciable: false
    IsMasked: false
    Name: "ProductBase4",
    Count: '...',
    Subclasses: [...]
}

I want to create a JSON object recursively from the array above. It will look like this:

[
  {
    name: 'Product',
    Count: 9,
    children: [
      {name: 'Product2'},
      {name: 'Product3'},
    ]
  }, {
    name: 'Product4',
    children: [
      {
        name: '...',
        Count: 'Sum of Count in all children by level'
        children: [
          {name: '...'},
          {name: '...'},
        ]
      }
    ]
  },
];

Here is my recursively function in typescript but it doesn't work as expected. How can I fix this?

recursive(data, stack: TreeNode[]) {
    let elt: TreeNode = {name:'', children: []}
    if(data.Subclasses.length > 0) {
      elt.name = data.Description;
      data.Subclasses.forEach(element => {
        elt.children.push({name: element.Description});
        this.recursive(element, stack);
      });
    }else {
      elt.name = data.Description;
    }
    stack.push(elt);
  }
like image 969
Wassim Sboui Avatar asked Sep 13 '25 07:09

Wassim Sboui


2 Answers

You can use the map function together with the recursive function like this:

function MapObject(object) {
  if(!object || object.length < 0) return [];

  return object.map(obj => { return {
      name: obj.Description,
      children: MapObject(obj.Subclasses)
  }});
}

Follows a full work example:

var example = [{
    Description: "Product",
    IsInstanciable: false,
    IsMasked: false,
    Name: "ProductBase",
    Subclasses: [{
      Description: "Product2",
      IsInstanciable: false,
      IsMasked: false,
      Name: "ProductBase2",
      Subclasses:[]
    },
    {
      Description: "Product3",
      IsInstanciable: false,
      IsMasked: false,
      Name: "ProductBase3",
      Subclasses:[]
    }]
},
{
    Description: "Product4",
    IsInstanciable: false,
    IsMasked: false,
    Name: "ProductBase4"
}]

function MapObject(object) {
  if(!object || object.length < 0) return [];
  
  return object.map(obj => { return {
      name: obj.Description,
      children: MapObject(obj.Subclasses)
  }});
}

console.log(MapObject(example));
like image 188
Ricardo Rocha Avatar answered Sep 14 '25 22:09

Ricardo Rocha


I would break this down into a few separate functions with clear responsibilities:

const sum = (ns) => 
  ns .reduce ((a, b) => a + Number (b), 0)

const getCount = (product) => 
  (product .Count || 0) + sum ((product .Subclasses || []) .map (getCount))

const extract = (product) => 
  ({
    name: product .Name,
    count: getCount (product),
    children: product .Subclasses .map (extract)
  })

const extractAll = (products) => 
  products .map (extract)

const products = [{"Description": "Product", "IsInstanciable": false, "IsMasked": false, "Name": "ProductBase", "Subclasses": [{"Count": 5, "Description": "Product2", "IsInstanciable": false, "IsMasked": false, "Name": "ProductBase2", "Subclasses": []}, {"Count": 4, "Description": "Product3", "IsInstanciable": false, "IsMasked": false, "Name": "ProductBase3", "Subclasses": []}]}, {"Count": 3, "Description": "Product4", "IsInstanciable": false, "IsMasked": false, "Name": "ProductBase4", "Subclasses": []}]

console .log (
  extractAll (products)
)

(Note that I changed the case of the output count property. It just seemed more consistent with name and children.)

We recur through the object to get its Count property, and we recur separately to get the rest of the object. While there might be a way to combine these, I would expect that the code would be significantly more complex.

Note that I assumed that all Count properties are actually numeric, although your example above includes a string. If you do have values like "3", then getCount will need to be a little more sophisticated.

like image 37
Scott Sauyet Avatar answered Sep 14 '25 20:09

Scott Sauyet