user992731
user992731

Reputation: 3520

Iterate through Multidimensional array in plain Javascript

I have the following Json file and I am trying to iterate through everything however only 1 item from the nested array is showing up. Not sure what I am doing wrong here.

JSON:

{
   "items":[
      {
         "label":"red",
         "url":"red",
         "items":[

         ]
      },
      {
         "label":"blue",
         "url":"#/blue",
         "items":[
            {
               "label":"green",
               "url":"#/green"
            },
            {
               "label":"yellow",
               "url":"#/yellow"
            },
            {
               "label":"pink",
               "url":"#/pink"
            }
         ]
      },......

JS:

var d = document, 
        main = d.getElementsByTagName('nav')[ 0 ],
        ul = d.createElement( 'ul' ),
        i;

        main.appendChild( ul );

        axios.get('../data/colors.json')
        .then(function (response) {
            console.log(response.data.items);

            for( var i in response.data.items ){
                var li = d.createElement( 'li' ); 
                if(response.data.items[i].items.length){
                    li.innerHTML = "<a class='meta'>" + response.data.items[i].label + "<div>" + response.data.items[i].items[i].label + "</div>" + "</a>"; // create a new li element
                }
                else {
                    li.innerHTML = "<a class='meta'>" + response.data.items[i].label + "</a>"; // create a new li element
                }
                ul.appendChild( li );// every time append a new item

            }

        })
        .catch(function (error) {
            console.log(error);
        });

Desired output:

<ul>
<li>red<li>
<li>blue
  <div>green </div>
  <div>yellow </div>
  <div>pink</div>
</li>
</ul>

Upvotes: 1

Views: 5362

Answers (5)

Mark
Mark

Reputation: 92440

You can do this with a simple recursive function that accepts the html element you want to append to and an object. An advantage of this approach is that will work to arbitrary depths and it maintains the nesting rather than flattening everything out. Most of the work is manipulating the HTML — other than that it's a very simple idea:

let obj = {"items":[{"label":"red","url":"red","items":[]},{"label":"blue","url":"#/blue","items":[{"label":"green","url":"#/green"},{"label":"yellow","url":"#/yellow"},{"label":"pink","url":"#/pink"}]}]}

function addItems(parent, obj) {
    let keys = Object.keys(obj)
    for (key of keys) {
      let o = obj[key]      
      let li = document.createElement('li')
      if (Array.isArray(o)) {
        let text = document.createTextNode(key);
        li.appendChild(text)
        let ul = document.createElement('ul')
        o.forEach(item => addItems(ul, item))
        li.appendChild(ul)
      } else {
        li.innerText = key + ': ' + o
      }
      parent.appendChild(li)
    }
}

let list = document.getElementById('1')
addItems(list, obj)
<ul id="1">
</ul>

Upvotes: 1

Kyle
Kyle

Reputation: 4014

Here's my take. You'll want to use recursion to print out the list.

function printList(list,container){
    var ul = document.createElement('ul');
    list.forEach(function(item){
        var li = document.createElement('li');
        li.innerHTML = '<div class="meta">' + item.label + '</div>';
        if(item.hasOwnProperty('items')){
            printList(item.items,li);
        }
        ul.appendChild(li);
    });
    container.appendChild(ul);
}

To use it:

axios.get('../data/colors.json').then(function (response) {
    printList(response.data.items,document.body);
})

function printList(list,container){
    var ul = document.createElement('ul');
    list.forEach(function(item){
        var li = document.createElement('li');
        li.innerHTML = '<div class="meta">' + item.label + '</div>';
        if(item.hasOwnProperty('items')){
            printList(item.items,li);
        }
        ul.appendChild(li);
    });
  container.appendChild(ul);
}

var items = {
   "items":[
      {
         "label":"red",
         "url":"red",
         "items":[

         ]
      },
      {
         "label":"blue",
         "url":"#/blue",
         "items":[
            {
               "label":"green",
               "url":"#/green"
            },
            {
               "label":"yellow",
               "url":"#/yellow"
            },
            {
               "label":"pink",
               "url":"#/pink"
            }
         ]
      }
     ]
}

printList(items.items,document.body);

console.log();

Upvotes: 1

Vivek
Vivek

Reputation: 1513

Here's how you can do it -

var data = {
  "items": [{
      "label": "red",
      "url": "red",
      "items": [

      ]
    },
    {
      "label": "blue",
      "url": "#/blue",
      "items": [{
          "label": "green",
          "url": "#/green"
        },
        {
          "label": "yellow",
          "url": "#/yellow"
        },
        {
          "label": "pink",
          "url": "#/pink"
        }
      ]
    }
  ]
}

var createList = function(response) {
  var d = document,
    main = d.getElementById('list'),
    ul = d.createElement('ul'),
    i;

  main.appendChild(ul);

  //console.log(response.data.items);

  for (var i in response.data.items) {
    var li = d.createElement('li');
    if (response.data.items[i].items.length) {
      li.innerHTML = "<a class='meta'>" + response.data.items[i].label + "</a>"; // create a new li element
      ul.appendChild(li); // every time append a new item
      var innerul = d.createElement('ul');
      for (var j in response.data.items[i].items) {
        var innerli = d.createElement('li');
        innerli.innerHTML = "<div>" + response.data.items[i].items[j].label + "</div>"; // create a new li element
        innerul.appendChild(innerli);

      }
      li = innerul;
    } else {

      li.innerHTML = "<a class='meta'>" + response.data.items[i].label + "</a>"; // create a new li element
      ul.appendChild(li); // every time append a new item
    }
    ul.appendChild(li); // every time append a new item
  }

}

createList({
  data: data
});
<div id="list"></div>

Upvotes: 1

Dennis Rasmussen
Dennis Rasmussen

Reputation: 520

You'll want to iterate/loop over the nested items, which you aren't doing. Basically add another loop for the 2nd level of items.

Here's a cleaned up version: http://jsfiddle.net/DennisRas/zyn52p8v/
Note: I've removed the .axios module to show what's important.

const data = {
   "items":[
      {
         "label":"red",
         "url":"red",
         "items":[

         ]
      },
      {
         "label":"blue",
         "url":"#/blue",
         "items":[
            {
               "label":"green",
               "url":"#/green"
            },
            {
               "label":"yellow",
               "url":"#/yellow"
            },
            {
               "label":"pink",
               "url":"#/pink"
            }
         ]
      },
      {
         "label":"blue",
         "url":"#/blue",
         "items":[
            {
               "label":"green",
               "url":"#/green"
            },
            {
               "label":"yellow",
               "url":"#/yellow"
            },
            {
               "label":"pink",
               "url":"#/pink"
            }
         ]
      },
      {
         "label":"blue",
         "url":"#/blue",
         "items":[
            {
               "label":"green",
               "url":"#/green"
            },
            {
               "label":"yellow",
               "url":"#/yellow"
            },
            {
               "label":"pink",
               "url":"#/pink"
            }
         ]
      }
    ]
};

const nav = document.querySelector('nav');
const ul = document.createElement( 'ul' );

nav.appendChild(ul);

data.items.forEach(function(item) {
  const li = document.createElement('li');
  let html = "<a class='meta'>" + item.label;

  if (item.items && item.items.length) {
    item.items.forEach(function(subitem) {
        html += "<div>" + subitem.label + "</div>";
    });
  }

  html += "</a>";

  li.innerHTML = html;
  ul.appendChild(li);// every time append a new item
});

console.log(data.items);

Upvotes: 3

saucel
saucel

Reputation: 96

I don't think that you are looping over the nested items array. You are only looping over the parent array.

If there are items in the nested array, add another loop for it. For example

for (var j in response.data.items[i].items) {
  li.innerHTML = "<a class='meta'>" + response.data.items[i].label + "<div>" + response.data.items[i].items[j].label + "</div>" + "</a>"; // create a new li element
}

I think there are better patterns to do a for loop, but I am just keeping your pattern here. Basically root cause, you aren't looping over the nested items.

Upvotes: 0

Related Questions