javascriptjqueryarraysobjectd3.js

Converting into a hierarchical array in javascript


Fiddle Example

I want to convert this JSON data

var data = [
  {
    "computer": 24,
    "brand": "Italy A",
    "phone": 0,
    "country": "Italy"
  },
  {
    "brand": "Italy C",
    "computer": 0,
    "phone": 0,
    "country": "Italy"
  },
  {
    "brand": "Brazil B",
    "computer": 0,
    "phone": 22,
    "country": "Brazil"
  },
  {
    "computer": 0,
    "brand": "Brazil D",
    "phone": 62,
    "country": "Brazil"
  },
  {
    "computer": 34,
    "brand": "US E",
    "phone": 41,
    "country": "US"
  }
];

into a hierarchical form for a d3 graph:

{
  "name": "categories",
  "children": [
    {
      "name": "phone",
      "children": [
        {
          "name": "US",
          "children": [
            {
              "brand": "US E",
              "size": 41
            }
          ]
        },
        {
          "name": "Brazil",
          "children": [
            {
              "brand": "Brazil B",
              "size": 22
            },
            {
              "brand": "Brazil D",
              "size": 62
            }
          ]
        },
        {
          "name": "Italy",
          "children": []
        }
      ]
    },
    {
      "name": "computer",
      "children": [
        {
          "name": "US",
          "children": [
            {
              "brand": "US E",
              "size": 34
            }
          ]
        },
        {
          "name": "Brazil",
          "children": []
        },
        {
          "name": "Italy",
          "children": [
            {
              "brand": "Italy A",
              "size": 24
            }
          ]
        }
      ]
    }
  ]
}

I came up with this code to generate the format:

function group_children(data){
    var categories = ["phone","computer"];
    var countries = ["US","Brazil","Italy"];
    var object = {name:"categories",children:[]};
    for(var c =0; c < categories.length;c++){
      object.children.push({"name":categories[c],children:[]});

     for(var con = 0;con < countries.length;con++){
       object.children[c].children.push({"name":countries[con],"children":[]});
     }
    }

    for(var i = 0;i < data.length;i++){
     var row = data[i];
     for(var c =0; c < categories.length;c++){
      for(var con = 0;con < countries.length;con++){
        var cat_key = categories[c],
            country_key = countries[con];
        if(row[cat_key] > 0){
          if(object.children[c].name == cat_key && row.country == country_key){  object.children[c].children[con].children.push({brand:row["brand"],size:row[cat_key]});        
          }
        }
      }   
    }
 }
 return object;
}

Is it possible , during the iteration, not to push a country into the brand or computer's children array if the country's children array is empty?

For example, these objects should be removed

        // computer
        {
          "name": "Brazil",
          "children": []
        }
        // phone:

        {
          "name": "Italy",
          "children": []
        }

Here's the part that push each country into each category's children array:

    for(var c =0; c < categories.length;c++){
      object.children.push({"name":categories[c],children:[]});

     for(var con = 0;con < countries.length;con++){
       object.children[c].children.push({"name":countries[con],"children":[]});
     }
    }

My approach is probably wrong, so any other suggestions converting the data into that hierarchical form is also appreciated.


Solution

  • Check this fiddle, is this what you're looking for? I decided to go for a different approach to the one you followed, hope you don't mind. I've commented the code so that it's clearer:

    var result = {
        name: "categories",
        children: [{
            "name": "phone",
                "children": []
        }, {
            "name": "computer",
                "children": []
        }]
    };
    
    $.each(data, function (index, item) {// Go through data and populate the result object.
        if (+item.computer > 0) { // Computer has items.
            filterAndAdd(item, result.children[1], "computer");
        }
        if (+item.phone > 0) { // Phone has items.
            filterAndAdd(item, result.children[0], "phone");
        }
    });
    
    function filterAndAdd(item, result_place, type) {// Search and populate.
        var i = -1;
        $.each(result_place.children, function (index,a) {
            if( a.name === item.country ) {
                i = index;
                return false;
            }
        });
    
        if (i > -1) {// Country already exists, add to children array.
            result_place.children[i].children.push({
                "brand": item.brand,
                    "size": item[type]
            });
        } else {// Country doesn't exist, create it.
            result_place.children.push({
                "name": item.country,
                    "children": [{
                    "brand": item.brand,
                        "size": item[type]
                }]
            });
        }
    }
    

    Hope it helps.