Loewe
Loewe

Reputation: 13

Generating multidimensional array/object from nested html list

I want to generate an Array, that represent the structure of an ordered list.

The list could be something like:

<ol class="list">
    <li><p>1</p>
        <ol>
            <li><p>1.1</p></li>
            <li><p>1.2</p></li>
            <li><p>1.3</p>
                <ol>
                    <li><p>1.3.1</p></li>
                </ol>
            </li>
            <li><p>1.4</p></li>
        </ol>
    </li>
    <li><p>2</p></li>
    <li><p>3</p></li>
</ol>

I use the following Javascript/Jquery function to traverse this list (based on this answer: https://stackoverflow.com/a/18084008/11995425 )

var count = 0;
var pages = [];
var parentStack = [];

var result = {};

parentStack.push(0);

function createNewLevel(obj) {
    var obj = obj || $('.list');

    if (obj.prop('tagName') == 'P') {
        ++count;

        pages.push({
            pId: parentStack[parentStack.length - 1],
            urlStr: obj.text(), myId: count
        });
    }

    if(obj.children().length > 0 ) {

        obj.find('> li').each(function(i){

            $(this).children().each(function(j){

                if($(this).prop('tagName') == 'OL') {
                    parentStack.push(count);
                }

                createNewLevel($(this));

                if($(this).prop('tagName') == 'OL') {
                    parentStack.pop();
                }
            });     
        })
    }
}
createNewLevel();

This generates an array:

​0: Object { pId: 0, urlStr: "1", myId: 1 }
1: Object { pId: 1, urlStr: "1.1", myId: 2 }
2: Object { pId: 1, urlStr: "1.2", myId: 3 }
3: Object { pId: 1, urlStr: "1.3", myId: 4 }
4: Object { pId: 4, urlStr: "1.3.1", myId: 5 }
​5: Object { pId: 1, urlStr: "1.4", myId: 6 }
6: Object { pId: 0, urlStr: "2", myId: 7 }
7: Object { pId: 0, urlStr: "3", myId: 8 }

pId references myId as parent.

I am not able to transform this to an multi array. At the end i pass this array (json.stringify) via ajax to PHP. Optimally this array should be generated while running "createNewLevel". But it is also possible to transform it afterwards in PHP. The result should look like:

array(5) {
  [0]=>
  array(2) {
    ["desc"]=>
    string(1) "1"
    ["children"]=>
    array(0) {
    }
  }
  [1]=>
  array(2) {
    ["desc"]=>
    string(1) "2"
    ["children"]=>
    array(4) {
      [0]=>
      array(2) {
        ["desc"]=>
        string(3) "2.1"
        ["children"]=>
        array(0) {
        }
      }
      [1]=>
      array(2) {
        ["desc"]=>
        string(3) "2.2"
        ["children"]=>
        array(0) {
        }
      }
      [2]=>
      array(2) {
        ["desc"]=>
        string(3) "2.3"
        ["children"]=>
        array(3) {
          [0]=>
          array(2) {
            ["desc"]=>
            string(5) "2.3.1"
            ["children"]=>
            array(0) {
            }
          }
          [1]=>
          array(2) {
            ["desc"]=>
            string(5) "2.3.2"
            ["children"]=>
            array(0) {
            }
          }
          [2]=>
          array(2) {
            ["desc"]=>
            string(5) "2.3.3"
            ["children"]=>
            array(0) {
            }
          }
        }
      }
      [3]=>
      array(2) {
        ["desc"]=>
        string(3) "2.4"
        ["children"]=>
        array(0) {
        }
      }
    }
  }
  [2]=>
  array(2) {
    ["desc"]=>
    string(1) "3"
    ["children"]=>
    array(0) {
    }
  }
  [3]=>
  array(2) {
    ["desc"]=>
    string(1) "4"
    ["children"]=>
    array(0) {
    }
  }
  [4]=>
  array(2) {
    ["desc"]=>
    string(1) "5"
    ["children"]=>
    array(0) {
    }
  }
}

Upvotes: 1

Views: 121

Answers (1)

Nenad Vracar
Nenad Vracar

Reputation: 122047

You can use reduce method and recursion to generate nested structure based on your html. If child element has ol then you call generate function where the element parameter will be that ol element.

function generate(element, pid = 0) {
  return [...element.children].reduce((r, li) => {
    const p = li.querySelector('p');
    const ol = li.querySelector('ol');
    const obj = {pid};

    if (p) obj.text = p.textContent;
    if (ol) obj.children = generate(ol, pid + 1);

    r.push(obj);
    return r;
  }, [])
}

const result = generate(document.querySelector('.list'))
console.log(JSON.stringify(result, 0, 4))
<ol class="list">
    <li><p>1</p>
        <ol>
            <li><p>1.1</p></li>
            <li><p>1.2</p></li>
            <li><p>1.3</p>
                <ol>
                    <li><p>1.3.1</p></li>
                </ol>
            </li>
            <li><p>1.4</p></li>
        </ol>
    </li>
    <li><p>2</p></li>
    <li><p>3</p></li>
</ol>

Upvotes: 1

Related Questions