mchd
mchd

Reputation: 3163

Creating a UL from nested array

I'm taking an intro JS course at my college and I have to build a <ul> with a nested array through JS. I've already built an <ol> with a regular array for the first part of the assignment. This is what I did:

window.onload = function(){

    fruits.sort();

    var ol = "<ol>"
    for(var i = 0; i < fruits.length; i++){
        ol+= "<li>" + fruits[i] + "</li>"
    }ol += "</ol>"

    var myContainer = document.querySelector("#olContainer").innerHTML = ol;
};

For this array:

var fruits = [ "Apples","Oranges","Pears","Grapes","Pineapples","Mangos" ];

and the current nested array I'm working with, is this:

var directory = [
    {type: "file", name: "file1.txt"},
    {type: "file", name: "file2.txt"},
    {type: "directory", name: "HTML Files", files: [{type: "file", name: "file1.html"},{type: "file", name: "file2.html"}]},
    {type: "file", name: "file3.txt"},
    {type: "directory", name: "JavaScript Files", files: [{type: "file", name: "file1.js"},{type: "file", name: "file2.js"},{type: "file", name: "file3.js"}]}
];

I am thinking that using a switch statement would be better but I don't know how to iterate through each line of code and extracting the nested lists to indent them as a sublist. I am required to build upon my previous JS function for the <ol> list

Upvotes: 0

Views: 492

Answers (3)

Taki
Taki

Reputation: 17654

Go recursive! It's basically a function that calls itself until certain condition, more details: https://www.sitepoint.com/recursion-functional-javascript/

Recursion is a technique for iterating over an operation by having a function call itself repeatedly until it arrives at a result. Most loops can be rewritten in a recursive style, and in some functional languages this approach to looping is the default.

you pass the directory to the function and loop through the first level values, if it's a file you add it to the list, otherwise you call the same function but you pass the second level list which is the value (the list of files), adding another nested ul, and so on ..

See the snippet below (based on your previous JS function for the <ol> list)

var directory = [
    {type: "file", name: "file1.txt"},
    {type: "file", name: "file2.txt"},
    {type: "directory", name: "HTML Files", files: [{type: "file", name: "file1.html"},{type: "file", name: "file2.html"}]},
    {type: "file", name: "file3.txt"},
    {type: "directory", name: "JavaScript Files", files: [{type: "file", name: "file1.js"},{type: "file", name: "file2.js"},{type: "file", name: "file3.js"}]}
];

function createUl(list){
	
    var myUl = "<ul>";

    for(var i = 0; i < list.length; i++){
      if(list[i].type == "file")
        myUl+= "<li>" + list[i].name + "</li>";
      else{ // it's a directory
        myUl+= "<li>" + list[i].name + "</li>";
        myUl += createUl(list[i].files); // call the same function with the list of files
      }
    }

    myUl += "</ul>";
    
    return myUl;
}

var ulList = createUl(directory);
document.querySelector("#ulContainer").innerHTML = ulList;
<div id="ulContainer">

</div>

Upvotes: 3

CertainPerformance
CertainPerformance

Reputation: 370829

Use recursion. It's much cleaner to create elements directly and assign to their textContent than to type out HTML markup (you don't know for sure whether an input's text is safe to insert as HTML or not, after all):

const input = [
    {type: "file", name: "file1.txt"},
    {type: "file", name: "file2.txt"},
    {type: "directory", name: "HTML Files", files: [{type: "file", name: "file1.html"},{type: "file", name: "file2.html"}]},
    {type: "file", name: "file3.txt"},
    {type: "directory", name: "JavaScript Files", files: [{type: "file", name: "file1.js"},{type: "file", name: "file2.js"},{type: "file", name: "file3.js"}]}
];

const outerUL = document.querySelector('ul');
appendObjToUL(input, outerUL);
function appendObjToUL(arr, ul) {
  arr.forEach(item => {
    ul.appendChild(document.createElement('li')).textContent = item.name;
    if (item.type === 'file') return;
    const dirLI = ul.appendChild(document.createElement('li'));
    const dirUL = dirLI.appendChild(document.createElement('ul'));
    appendObjToUL(item.files, dirUL);
  });
}
<ul></ul>

Upvotes: 2

Kavindra
Kavindra

Reputation: 1707

You can do it with 2 for loops(nested for loops). Implement the same kind of for loop if the directory[i] has a files array.

var directory = [
    {type: "file", name: "file1.txt"},
    {type: "file", name: "file2.txt"},
    {type: "directory", name: "HTML Files", files: [{type: "file", name: "file1.html"},{type: "file", name: "file2.html"}]},
    {type: "file", name: "file3.txt"},
    {type: "directory", name: "JavaScript Files", files: [{type: "file", name: "file1.js"},{type: "file", name: "file2.js"},{type: "file", name: "file3.js"}]}
];

window.onload = function(){
    var ol = "<ol>";
    for(var i = 0; i < directory.length; i++){
        ol += "<li>" + directory[i].name;
        if(directory[i].files) {
          ol += "<ol>";
          for(var j = 0; j < directory[i].files.length; j++) {
            ol += "<li>" + directory[i].files[j].name + "</li>";
          }
          ol += "</ol>";
        }
        ol += "</li>";
    }
    ol += "</ol>";

    var myContainer = document.querySelector("#olContainer").innerHTML = ol;
};
<div id="olContainer"></div>

Upvotes: 1

Related Questions