Nathaniel Alconcel
Nathaniel Alconcel

Reputation: 33

Recursive loop through nested array of objects to write nested list

Given a nested array of objects, I am trying to write in a nested unordered list.

The arrays themselves are organized so that every new children property, starts a new array of objects. The function needs to be able to support n level depth.

My current solution can recursively write all the properties I need now, but appends the < li> tags at the wrong < ul>.

I think this is because of :

  var lowUL = targetUL.querySelector('ul');

which I use in my recursive base case to append < li> too. It only selects the first < ul> tag it finds, and not the dynamically created < ul> tag from that iteration in the for loop.

// DATA
const org1_depts = [
  {
    name: 'accounting',
    children: [
      { name: 'accounting payable', children: [] },
      { name: 'accounting receivable', children: [] },
    ],
  },
  {
    name: 'finance',
    children: [],
  },
]

const org2_depts = [
  {
    name: 'accounting',
    children: [
      { name: 'accounting payable', children: [] },
      {
        name: 'accounting receivable',
        children: [{ name: 'cash', children: [] }, { name: 'check', children: [] }],
      },
    ],
  },
  {
    name: 'finance',
    children: [{ name: 'investment', children: [] }],
  },
]

// FUNCTION

function listOrg(orgData,targetUL) {
  var i;
  for (i = 0; i < orgData.length; i++) {
    if (orgData[i].hasOwnProperty('name')) {
      // Take out the text from the .name property
      var nameText = document.createTextNode(orgData[i].name);
      // Define a new <li> tag
      var newLI = document.createElement('li');
      // Append text to new <li> tage - newLI
      newLI.appendChild(nameText);
      // Append element to <ul> tag
      targetUL.appendChild(newLI);
    }

    if (orgData[i].hasOwnProperty('children')) {
      // Define new <ul> tag
      var newUL = document.createElement('ul');
      // Append new <ul> tag
      var lowUl = targetUL.appendChild(newUL);
      // Select new lower level <ul> tag
      var lowUL = targetUL.querySelector('ul');
      // Recurse
      listOrg(orgData[i].children,lowUL);
    }
  }
}

// CALL FUNCTION
listOrg(org1_depts,document.getElementById("org1"));
listOrg(org2_depts,document.getElementById("org2"));
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>
<body>
<ul id='org1'>
  Organization 1
</ul>

<ul id='org2'>
  Organization 2
</ul>

</body>
</html>

Above the child name properties from "accounting receivable" are being placed inside of "accounting payable", which is wrong.

Upvotes: 1

Views: 1798

Answers (1)

Nithya Rajan
Nithya Rajan

Reputation: 4884

When you recursively call the listOrg function, you should send the newUL variable as a parameter instead of lowUL. listOrg(orgData[i].children,newUL) this will target the newly created ul, you have no need to use querySelector

// DATA
const org1_depts = [
  {
    name: 'accounting',
    children: [
      { name: 'accounting payable', children: [] },
      { name: 'accounting receivable', children: [] },
    ],
  },
  {
    name: 'finance',
    children: [],
  },
]

const org2_depts = [
  {
    name: 'accounting',
    children: [
      { name: 'accounting payable', children: [] },
      {
        name: 'accounting receivable',
        children: [{ name: 'cash', children: [] }, { name: 'check', children: [] }],
      },
    ],
  },
  {
    name: 'finance',
    children: [{ name: 'investment', children: [] }],
  },
]

// FUNCTION

function listOrg(orgData,targetUL) {
  var i;
  for (i = 0; i < orgData.length; i++) {
    if (orgData[i].hasOwnProperty('name')) {
      // Take out the text from the .name property
      var nameText = document.createTextNode(orgData[i].name);
      // Define a new <li> tag
      var newLI = document.createElement('li');
      // Append text to new <li> tage - newLI
      newLI.appendChild(nameText);
      // Append element to <ul> tag
      targetUL.appendChild(newLI);
    }

    if (orgData[i].hasOwnProperty('children')) {
      // Define new <ul> tag
      var newUL = document.createElement('ul');
      // Append new <ul> tag
      var lowUl = targetUL.appendChild(newUL);
      // Select new lower level <ul> tag
      var lowUL = targetUL.querySelector('ul');
      // Recurse
      listOrg(orgData[i].children,newUL );
    }
  }
}

// CALL FUNCTION
listOrg(org1_depts,document.getElementById("org1"));
listOrg(org2_depts,document.getElementById("org2"));
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>
<body>
<ul id='org1'>
  Organization 1
</ul>

<ul id='org2'>
  Organization 2
</ul>

</body>
</html>

Upvotes: 3

Related Questions