30

The other day I thought I saw an object iterator in jQuery that had a flag that could be set to recursively iterate over child objects. I thought it was part of jQuery.each(), but now I don't see that capability in the docs.

Is there any such iterator in jQuery that can be automatically recursive?

(I know how to do it in javascript. Just wondering if I actually saw what I thought I saw.)

Thanks much!

EDIT: To be clear, I was thinking of a utility method like jQuery.each() that will iterate recursively over javascript objects and their nested objects.

Given the example below, the each() method would iterate over all objects, including the nested one in myobj.obj2.key2.

I could have sworn that I saw something in jQuery docs about that, but now I can't find it.

Thanks.

var myobj = {
    obj1: {key1:'val1', key2:'val2'},
    obj2: {key1:'val1', key2: {nest1:'val1', nest2:'val2', nest3:'val3'}},
    obj3: {key1:'val1', key2:'val2'}
}

$jQuery.each(myobj, function(key,val) {
    // Code to run over each key/val pair
    // Does so recursively to include all nested objects
})
user113716
  • 318,772
  • 63
  • 451
  • 440
  • what would you expect this to look like? which key,val pairs are shown? – tooleb Feb 05 '10 at 13:50
  • I suppose I just thought that it would do a 'typeof' test or something, and jump into an object when it finds one. The key/val pairs would be for whichever object it was currently in. – user113716 Feb 05 '10 at 14:02

4 Answers4

39

The .find('selector') method is basically a recusive version of .children(), and will find any descendant object that matched the selector, as opposed to .children() which only finds objects in the first level of descendants.

2nd EDIT (I phrased badly the first time, and messed up the code a bit!):

Ok, I don't think this functionality as a flag would make sense: you can quite happily recurse through that object forever (believe me, I broke firefox doing it), so you need some sort of interaction to make sure you only recurse when the child object is a valid recursion candidate.

What you need to do is simply split the function like so:

var myobj = {
  obj1: {
    key1: 'val1',
    key2: 'val2'
  },
  obj2: {
    key1: 'val1',
    key2: {
      nest1: 'val1',
      nest2: 'val2',
      nest3: 'val3'
    }
  },
  obj3: {
    key1: 'val1',
    key2: 'val2'
  }
}

$jQuery.each(myobj, function(key, val) {
  recursiveFunction(key, val)
});

function recursiveFunction(key, val) {
  actualFunction(key, val);
  var value = val['key2'];
  if (value instanceof Object) {
    $.each(value, function(key, val) {
      recursiveFunction(key, val)
    });
  }

}

function actualFunction(key, val) {
  /// do stuff
}
Mickael Lherminez
  • 679
  • 1
  • 10
  • 29
Ed James
  • 10,385
  • 16
  • 71
  • 103
  • Sorry Ed, I wasn't clear. I mean a utility like jQuery.each() that recursively iterates over javascript objects. Not over the DOM. – user113716 Feb 05 '10 at 13:26
  • No worries, I'm having a looksy myself now, this could be quite good functionality to have (for trees and stuff) – Ed James Feb 05 '10 at 13:51
  • Hi Ed. Could you please explain what you mean about the issue with different types? It would seem to me that it would rely on different types in order to detect the presence of an object. Also, I'm a little confused by the if() statement part of your answer, probably because I'm not thinking of the DOM at all. Rather just a javascript utility for behind the scenes sort of stuff (though it could, of course, interact with the DOM when you need it). Thanks. – user113716 Feb 05 '10 at 14:19
  • well, in theory a string can act like a KVP, so you'd recurse infinitely without the if statement (I just used completely the wrong code: I shouldn't be allowed near a computer when I've not eaten ;) ) See if that's any better for you. – Ed James Feb 05 '10 at 14:45
  • Yes, I see. And I knew how to recursively iterate as you did. I just thought that I saw that ability documented somewhere for jQuery. Imagine a $.each() method where you pass in the set of objects to be iterated, the function that would be executed on each pair, and a flag telling it to be recursive. If that flag is set, it would then call an if() statement during each iteration to test for an Object as your code did. It would just all be hidden in the $.each(). Now go eat a cookie! ;o) – user113716 Feb 05 '10 at 15:11
  • Haha, I'm well aware you know how to, you said in the OP, I just thought I'd illustrate my point with some code. I think there'd be situations where this is too difficult for an inbuilt method, and that in general it'd be more sensible to just write the code yourself: that way you can deal with things like the fact that val['key2'] won't work on the child elements because they're named differently! (bad example, but you get my drift) – Ed James Feb 05 '10 at 15:32
  • Now I need a cookie! ;oP I'll let you off the hook soon, but why would naming make any difference? Couldn't it just test each 'val' to see if it is an object, then if so, start a new iteration? I wouldn't think one would need to tell it quite so explicitly which ones to test. Or am I missing some other roadblock? Thanks again. – user113716 Feb 05 '10 at 15:50
  • I did say the naming was a bad example! I think it could just lead to undefined behaviour when some anonymous object was passed into your each, what happens if key2 happens to be an array of integers and you try and do a string comparison on them? It would be almost trivial to implement a catch-all recursive override for each(), as we've both said, so the fact that it hasn't been done is an indicator that it's unlikely to be useful in that form, or that it would cause more headaches than it salves! – Ed James Feb 05 '10 at 15:55
  • Ok. I see what you're saying. I guess I would assume that if a person set the flag, that they would also know and control the data stored in the objects. Anything and everything in jQuery can be misused. The point is that when used appropriately, it saves some coding. You've answered my question, though. There is no such functionality in jQuery. Thanks for your input. – user113716 Feb 05 '10 at 16:08
  • Trying this code, but right off the bat it seem you have to know which key names are objects, not simple values: `var value = val['key2'];` – TARKUS Jul 09 '17 at 11:52
9

A slightly simlified version of @Ed Woodcock's version above. They way I needed to use this was to output an HTML bulleted list with named links.

var list = "<ul>";
$.each(data, recurse);

function recurse(key, val) {
    list += "<li>";
    if (val instanceof Object) {
        list += key + "<ul>";
        $.each(val, recurse);
        list += "</ul>";
    } else {
        list += "<a href='" + val + "'>" + key + "</a>";
    }
    list += "</li>";
}
list += "</ul>";

$("#container").html(list);
Chris Barr
  • 29,851
  • 23
  • 95
  • 135
1

you can do this much easier as such

$(this).children().each(function(index, element) {
...
});
Justin T. Watts
  • 547
  • 1
  • 4
  • 16
0

This q and a was so helpful. Thank you. (I also appreciated the cookie reference. I'm still slogging through that book!).

Here is my version doing a "search and replace" for a simple i18n jQuery solution (this may be helpful to someone). It finds the term wrapped in a class replaces it if the term is in the dictionary.

Fiddle: http://jsfiddle.net/orolo/CeY5Y/11/

HTML:

In the <span class="i18n">Clear</span> and also <span class="i18n">Save Widget</span>. I'm <span class="i18n">On</span> the <span class="i18n">sub3</span> and <span class="i18n">PDF</span>.

javascript / jQuery:

var term = "";

var customDict = {
    "Level One": {
        "Clear": "1234",
        "CSV": "CSV",
        "First": "First",
        "Last": "Last",
        "Next": "Next",
        "On": "42",
        "Off": "Off",
        "PDF": "alpha",
        "Prev": "Prev",
        "Rows": "Rows",
        "Save Widget": "saver widgetor",
        "Stats": "statistiques",
        "sub": {
            "sub2": {
                "sub3": "inception"
            }
        }
    }
};

function recursiveLookup(key, val) {
    //test for a match between term and key
    if (key === term) {
        $('.i18n').each(function() {
            if ($(this).text() === key) {
              $(this).text(val);  
            }
        });
    }
    //val is an object or no match? recur
    if (val instanceof Object) {
        $.each(val, function(key1, val1) {
            recursiveLookup(key1, val1);
        });
    }
}

function convert() {    
    $('.i18n').each(function(key, val) {
        term = $(this).text();  
        $.each(customDict, function(key, val) {
            recursiveLookup(key, val);
        });

    });
}


/*call the function*/
convert();
orolo
  • 3,951
  • 2
  • 30
  • 30