Ext JS iterator functions
July 23, 2009 22 Comments
Ext JS has a number of handy iterator functions. Some, like Ext.each, you probably already know about, but there are a few others lurking around which can be useful in saving yourself a few lines of code. First, let’s recap Ext.each:
Ext.each
Ext.each applies a function to each member of an array. It’s basically a more convenient form of a for loop:
var people = ['Bill', 'Saul', 'Gaius']; //using each to detect Cylons: Ext.each(people, function(person, index) { var cylon = (index + 1) % 2 == 0; //every second man is a toaster alert(person + (cylon ? ' is ' : ' is not ') + 'a fraking cylon'); }); //is the same as for (var i=0; i < people.length; i++) { var person = people[i]; var cylon = (i + 1) % 2 == 0; //every second man is a toaster alert(person + (cylon ? ' is ' : ' is not ') + 'a frakin cylon'); };
Ext.iterate
Ext.iterate is like Ext.each for non-array objects. Use it wherever you would normally use a for .. in loop:
var ships = {'Bill': 'Galactica', 'Laura': 'Colonial One'}; Ext.iterate(ships, function(key, value) { alert(key + "'s ship is the " + value); }); //is the same as for (key in ships) { var value = ships[key]; alert(key + "'s ship is the " + value); }
Using Ext.iterate with an array is the same as calling Ext.each. Each and Iterate both take an optional third parameter, which is the scope to run the function in. Another advantage over using the for construct is that you can easily reuse the same function:
var myFunction = function(item, index) { //does some clever thing } Ext.each(people, myFunction); Ext.each(['another', 'array'], myFunction);
Ext.pluck
Ext.pluck grabs the specified property from an array of objects:
var animals = [ {name: 'Ed', species: 'Unknown'}, {name: 'Bumble', species: 'Cat'}, {name: 'Triumph', species: 'Insult Dog'} ]; Ext.pluck(animals, 'species'); //returns ['Unknown', 'Cat', 'Insult Dog'] Ext.pluck(animals, 'name'); //returns ['Ed', 'Bumble', 'Triumph']
Ext.invoke
Invoke allows a function to be applied to all members of an array, and returns the results. Using our animals object from above:
var describeAnimal = function(animal) { return String.format("{0} is a {1}", animal.name, animal.species); } var describedAnimals = Ext.invoke(animals, describeAnimal); console.log(describedAnimals); // ['Ed is a Unknown', 'Bumble is a Cat', 'Triumph is a Insult Dog'];
Ext.invoke performs a similar job to Ruby’s collect method in making it easy to transform arrays. Any additional arguments passed to the Ext.invoke call will be passed as arguments to your function, in this case the describeAnimal function. Obviously your functions will be much more grammatically accurate than mine.
Ext.partition
Ext.Partition splits an array into two sets based on a function you provide:
var trees = [ {name: 'Oak', height: 20}, {name: 'Willow', height: 10}, {name: 'Cactus', height: 5} ]; var isTall = function(tree) {return tree.height > 15}; Ext.partition(trees, isTall); //returns: [ [{name: 'Oak', height: 20}], [{name: 'Willow', height: 10}, {name: 'Cactus', height: 5}] ]
The partition call above returns a 2-dimensional array with the first element containing all of the items for which the function returned true (tall trees in this case), and the second containing items for which the function return false.
Math functions
Finally, we have some simple math-related functions:
var numbers = [1, 2, 3, 4, 5]; Ext.min(numbers); //1 Ext.max(numbers); //5 Ext.sum(numbers); //15 Ext.mean(numbers); //3
While the built in functions don’t cater for all situations, they’re useful to have and to know about, and usually offer a more elegant approach than using the ‘for’ keyword.
Nice post, too! ;)Didn't know all of those methods.Which I don't like about these methods is the fact, that Ext doesn't add those methods to the object classes you perform those methods on. So, this is pretty much PHP style.I like more Prototype JS's and therefore Ruby's approach to add methods as 'each' directly to the Array class, which allows you to write [1, 2].each().That's why I actually like to use Ext JS with the Prototype JS adapter.I guess I'll write an Ext extension one day that adds such 'shortcuts' for the Ext methods to their respective class and removes the need for the Prototype JS dependency in my applications. Shouldn't be a big thing.
I also prefer Ruby's style of chaining method calls together, but sadly it can't be done safely in JavaScript, as augmenting Object breaks the "for key in values" construct.I think it can be ok to do that, so long as the implications are understood. I tend to stick to the side of caution though and not augment Object at all. Ext JS is very good at respecting that approach also.If you do write the extension you mention, be sure that everyone using it understands what the implications are 🙂
Strictly speaking, nobody should mess around with JavaScript Object. Leave it as is.
ExtJS approach is more professional.
I’m wondering why Ext adds the single `remove` method to Array.prototype? Extending Array or Function is safer than extending Object w/ respect to `for in`, but is a bit invasive for a library to do. There’s a case for a really developer-friendly library like prototype to make enumeration easier, but that’s not Ext so why pollute Array for just a single method? Is this just a mistake from the early days?
@rob agreed – for clarity I was referring to the style of code produced with Ruby’s built in enumerable functions, it’s secondary to global object pollution concerns though.
@mike as you say, augmenting Array is safe compared to augmenting Object because you don’t run ‘for … in’ on an array (unless you’re crazy). I’m not sure when it was added in but can’t really see a problem with it. Not sure I’d call it a ‘mistake’ either if it doesn’t have the negative consequences that augmenting Object has.
Always excellent to read pages like this. Bright & helpful! Thanks a lot
Ext JS add a “remove” function that must be ignored.
The Ext.each and Ext.iterate will not give the same result
if an arbitrairy element is added in the array.
ex:
layers["layer1", "layer2"];
layers[1000] = "layer3";
layers["special"] = "layer4";
With “Ext.each” and “Ext.iterate”, this will output the
first 2 layers following by 998 undefined and the
1000th layer. The “special” layer will be ignored.
With the “for (i in layers)”, it will give the
4 layers, but also the “remove” function added by
Ext JS.
Here is the code I’m using to see the content of the array without all unwanted Undefined:
for (i in layers) {
if (i != 'remove') {
layer = layers[i];
// Do something with the layer
}
}
Thanks alot… after 2 days struggle with my code i got the solution with this site.
Thank you…
Thank you for your post. I am a big Extjs fan and if all the best things were in the same framework it would be as great as all the best things were in all of us. What I know about Ext is, that you have to study hard but you will be rewarded for your efforts. Useless to compare it with another framework on the wrong arguments. Jquery is for “things” and Ext is for applications.
Quite hopeless to port an exsisting webapplication to extjs, when fully functional javascript code stop working because of extjs added array functions, well we decided not to use extjs on the project – The more i work with extjs, the less i like it.
@Illop I hope we win you back round with Ext JS 4. We’ve removed all native object augmentation
I just started watching Battlestar Galactica on NetFlix.. Love the reference…
This is a great article! I keep coming back, very useful.
One thing I also think that would be worth mentioning in this article is that Extjs each won’t handle break (or continue) like normal javascript, so instead you should use return false; (to break) or return true; (to continue)
@Ed Spencer, this a very great article, thank you!!
Pingback: extjs中の迭代だった | テクニカルブログ
Pingback: The Extjs iteration
@Ed Spencer, excellent article, as always. Your examples are really helpful. Thanks!
Nice post!
Typo on Ext.each line 12, instead of (index + 1) should be (i+1)
Thanks Ronald, fixed
This is a great post! Thank you.
The description on Ext.invoke() is inaccurate and the corresponding example doesn’t work. The 2nd arguments which is a method name is supposed to be a member of each element in the array, whereas in your example it implies that the methodName can be an external method you can define separately.
Ext.invoke([el1, el2], ‘enable’);
means that you can also do it manually as:
el1.enable();
el2.enable();