Search code examples
javascriptalgorithmloopsrecursionhtml-lists

How to generate ul li from a nested array?


I have the following code below and trying to find a way to generate <ul> and <li> to output an unordered list via Javascript.

Question(s):

  1. How can I parse the following array to generate a nested unordered list html?
  2. If I were to use recursion, how would I recursively iterate through all nested children to print a nested unordered list?

What I tried that didn't work

const data = [
    {
        name: '125313-7j',
    },
    {
        name: '584996-s7',
        _children: [
            {
                name: '747773-jw',
            },
            {
                name: '377526-zg',
                _children: [
                    {
                        name: '955330-wp',
                    },
                    {
                        name: '343693-9x',
                    },
                ],
            },
            {
                name: '638483-y2'
            },
        ],
    },
    {
        name: '306979-mx'
    },
    {
        name: '066195-o1',
        _children: [],
    },
];

$(function() {
let innerHtml = '<ul>';
data.forEach( item => {
  innerHTML += '<li>' + '<div>' + item.name + '</div>';
    if(item._children) {
      // ***ISSUE***  this doesn't print all the children so i cannot recursively get all the children
       console.log('children', item._children);
    }
});

innerHtml += '</ul>';
});

Expected output

<ul>
  <li> <div>Name1</div>
     <ul>
        <li><div>Name2</div>
         .... continue the next unordered list until all children have been displayed ...
      </ul>
   </li>
</ul>

Solution

  • For your requirement, the recursive solution seems more intuitive to me. Let me share my insights for approaching this:

    # Pseudocode
    
    # generateList(items)
    #   1. init: html<String> with opening "<ul>" tag. 
    #            Can also use array. See References section
    
    #   2. for item in items
    #       2.1. html += "<li><div>", item.name, "</div>"
    #       2.2. if item has _children and the length of _children is greater than 0
    #            2.2.1. html += result of calling generateNestedList recursively with item._children
    #       2.3. html += closing "<\li>"
    
    #   3. html += closing "</ul>"
    
    #   4. return html.
    # end function
    

    const data = [{
        name: '125313-7j',
      },
      {
        name: '584996-s7',
        _children: [{
            name: '747773-jw',
          },
          {
            name: '377526-zg',
            _children: [{
                name: '955330-wp',
              },
              {
                name: '343693-9x',
              },
            ],
          },
          {
            name: '638483-y2'
          },
        ],
      },
      {
        name: '306979-mx'
      },
      {
        name: '066195-o1',
        _children: [],
      },
    ];
    
    function generateList(items) {
      let html = '<ul>';
    
      items.forEach((item) => {
        html += '<li><div>' + item.name + '</div>';
        if (item._children?.length > 0) {
          html += generateList(item._children);
        }
        html += '</li>';
      });
    
      html += '</ul>';
      return html;
    }
    
    const innerHtml = generateList(data);
    // console.log(innerHtml);
    document.getElementById("list-container").innerHTML = innerHtml;
    <div id="list-container"></div>


    References:

    1. Array Join vs String Concat
    2. Optional Chaining(?.)