简体   繁体   中英

Creating a multi-level array from DOM elements' data attributes in jQuery

Ok this may be a simple loop question that I'm just overthinking (would be far from the first time) but I'm going to post this just incase someone has a quick answer for me.

I have an array of DOM elements like this:

<ul id="array">
  <li class='item' data-id="1">
    <ul class="child">
      <li class='item' data-id="2"></li>
      <li class='item' data-id="3"></li>
    </ul>
  </li>
  <li class='item' data-id="4"></li>
  <li class='item' data-id="5">
    <ul class="child">
      <li class='item' data-id="6"></li>
      <li class='item' data-id="7">
        <ul class="child">
          <li class='item' data-id="8"></li>
          <li class='item' data-id="9"></li>
          ...
        </ul>
      </li>
    </ul>
  </li>
</ul>

I want to loop through that using jQuery and come out with something like:

var result = [
  {
    "id":1,
    "children":[
      {
        "id":2,
        "children":[]
      }, 
      {
        "id":3,
        "children":[]
      }
    ]
  },
  { 
    "id": 4,
    "children":[]
  },
  {
    "id": 5,
    "children":[
      {
        "id":6,
        "children":[]
      }, 
      {
        "id":7,
        "children":[
          {
            "id":8,
            "children":[]
          }, 
          {
            "id":9,
            "children":[]
          }
        ]
      }
    ]
  }
]

(Note that is obviously spaced out for easy reading :) )

If that was the exact number of child ul items there would be, that would be an easy loop. The issue I'm having is that the list item tree could go as many levels down as a user wants (realistically, it will stop at some point but you get the idea) so it will need to continue to loop through the tree until there are no more.

You'll need a recursive function and map() method for this:

function extract() {
    var $this = $(this);

    return {
        id: $this.data('id'),
        children: $this
            .find('> .child > .item')
            .map(extract)
            .get()
    };
}

var output = $('#array > .item')
    .map(extract)
    .get();

Demo: http://jsfiddle.net/pj2C2/1/

This worked for me:

function arrayRecursive($arr) {
    var result = [];

    // with > .item you go only 1 level deeper per step
    $arr.find('> .item').each(function(index, item) {
        var obj = {};
        obj.id = $(item).attr('data-id');
        obj.children = arrayRecursive($(item).find('.child'));
        result.push(obj);
    });

    return result;
}

var result = arrayRecursive($('#array'));

// Test the result with a stringified alert
alert(JSON.stringify(result));

EDIT: Removed the if ($(item).find('.child').length > 0) so you have empty arrays as default.

Sounds like a recursion problem.

So you can loop through the li elements check to see if the child is a ul element if so another level of recursion else store the value at that level.

Some rough pseudo code

readElements($("#array").children());

function readElements(array) {
    var elementsArray = [];
    for (var i = 0; i < array.length; i++) {
       if ($($(array[i]).children()[0]).is("ul")) {
           return readElements($(array[i]).children());
       }
       elementsArray.push({ id: getId(), children: [] });
       // add id elementArrays
    }
    return elementsArray;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM