1

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.

3 Answers 3

3

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/

Sign up to request clarification or add additional context in comments.

1 Comment

Great work! Thank you! This is very concise and works great :)
2

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.

2 Comments

Works, but I would avoid creating new jQuery object inside the function like this: jsfiddle.net/nZ3BJ
I didn't choose this as the answer but did give it an upvote because it does work great! I find @Pavio's answer to be a bit cleaner, but thanks for doing this! Really appreciate the effort.
0

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;
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.