I've got some javascript for {} loops which I use repeatedly throughout the project, they are all similar to this:
for (var i = 0; i < things.length; i++) {
console.log(things[i]);
// This may be different in different areas of the project
}
I minified the code, but the loops take up a lot of the minified code. Is there a way to shorten the above code to something like this:
loop {
console.log(things[i]);
// This may be different in different areas of the project
}
Probably not the above, but you get the idea.
Any help would be much appreciated :)
If you are repeatedly printing different arrays, you could make a function for it to cut your repetition down:
function printArray(arr) {
for (var i = 0; i < arr.length; i++) {
console.log(arr[i]);
}
}
then call like:
printArray(things);
If you are doing more than just printing and want it to be more universal, you should use a callback, like this:
function loopArr(arr, cb) {
for (var i = 0; i < arr.length; i++) {
cb(arr[i]);
}
}
and this could be called like:
loopArr(thing, function (i) {
console.log(i);
});
Fiddle
Also there are tools that can already do this for you, for instance if you are using (or would want to use) jQuery, you could use jQuery.each()
A jQuery-ish way to do that:
function each(arr, func) {
for ( var i = 0; i < arr.length; ++i ) {
func(arr[i]);
}
}
can be called like:
each( things, function(thing) { console.log(thing); } );
or
each( things, function(thing) { console.log(thing); alert(thing); } );
etc.
You'd have to pass in the item and the callback, but of course it's possible.
function loop (item, callback) {
for (var i = 0; i < item.length; i++) {
callback(item[i]);
}
}
Useage:
loop(things, function (item) {
console.log('do things here with each ' + item);
});
Also note that in more modern browsers you could simply do:
things.forEach(function (item) {
/* do whatever */
});
function forEach(array, func) {
var i = array.length;
while(i--) {
func(array[i]);
}
}
Everyone beat my to it, but here is another way to write it
function forEach(collection, callback){
var e;
for (var i = 0; e = collection[i++];) {
callback(e);
}
}
And its' usage:
var a = ["The", "Brown", "Cow"];
forEach(a, function(e) { console.log(e); });
Should be mentioned that there are tons of implementations of iterator functions. Your exact case my need to improve upon these.
Related
As part of an exericse, I'm re-writing underscore functions and testing them in jsfiddle. Every time I pass a callback function, I get "undefined".
My code is below:
each = function(collection, iterator) {
if(Array.isArray(collection)){
for (var i = 0; i < collection.length; i++) {
iterator(collection[i], i, collection);
}
} else {
for(var key in collection) {
iterator(collection[key], key, collection);
}
}
};
var numbers = [1,2,3,4];
var result = each(numbers, function(num) {
return num * 2;
});
console.log(result);
// undefined
Any idea what I'm doing wrong and why it's not outputting on jsfiddle?
You aren't doing anything wrong. Your each function isn't returning anything. This is fine, since each functions don't necessarily have to return anything. You might be thinking along the lines of a map or reduce function which compile the results of calling the callback on each item in the collection, and then return that compilation.
The callback you pass to an each function doesn't normally return anything. Think of an each like it's just syntactic sugar for a normal for loop; for loops don't return anything (obviously..), they just perform generic operations with the items in the collection and the variables that have been declared in the containing scope.
That being said, if you want to emulate underscore (or any good library), you will want to return the collection to enable chaining. From the underscore docs:
[each] Iterates over a list of elements, yielding each in turn to an iteratee function....Returns the list for chaining.
This is just good practice so as to avoid annoying the developers who may use your library and are used to being able to chain everything in JavaScript.
So all you'd need to do is put
return collection;
at the end of your each function and you're good. Cheers.
You are not aggregating the result of your operation that's why the result is not being result.
Here is a quick stab at how this can be fixed for arrays.
each = function(collection, iterator) {
var arr = [];
if(Array.isArray(collection)){
for (var i = 0; i < collection.length; i++) {
arr.push( iterator(collection[i], i, collection) );
}
return arr;
} else {
for(var key in collection) {
iterator(collection[key], key, collection);
}
}
};
var numbers = [1,2,3,4];
var result = each(numbers, function(num) {
return num * 2;
});
console.log(result);
jsfiddle
OK, let's keep it simple, I have the following :
function loopDaLoop(){
for (var i = 0; i < tempItemsLength; i++) {
var product = tempItems[i];
dust.render('product', product, addProductOrFinish);
}
}
and i'd like to get the current value of i inside my callback function
function addProductOrFinish(err, out) {
console.log(i); // undefined
}
I do know it's simple, really I do... Help?
Edit :
I know I'm supposed to use a closure so I tried and failed with this :
(function(i){
dust.render('product', product, addProductOrFinish);
};(i)
There are several different structures that could be used to solve this problem. The simplest is to use .bind() to add the desired parameter to the function call.
function loopDaLoop(){
for (var i = 0; i < tempItemsLength; i++) {
var product = tempItems[i];
dust.render('product', product, addProductOrFinish.bind(null, i));
}
}
function addProductOrFinish(i, err, out) {
console.log(i);
}
This will cause the value of this to change in addProductOrFinish if that was important. If so, you could work around that too, but it's not as simple.
Here's another approach using a closure that returns a function and preserves the value of this in case dust.render() is setting that:
function loopDaLoop(){
for (var i = 0; i < tempItemsLength; i++) {
var product = tempItems[i];
dust.render('product', product, getAddProductOrFinish(i));
}
}
function getAddProductOrFinish(loopArg) {
return function(err, out) {
return addProductOrFinish.call(this, loopArg, err, out);
}
}
function addProductOrFinish(i, err, out) {
console.log(i);
}
Or, if addProductOrFinish can be an inline function, then it can use the IIFE-type structure you tried like this:
function loopDaLoop(){
for (var i = 0; i < tempItemsLength; i++) {
var product = tempItems[i];
(function(i) {
dust.render('product', product, function(err, out) {
console.log(i);
// rest of your addProductOrFinish logic here
});
)(i);
}
}
In my project I have to do some two-deep loop procedure several (meaning a lot of) times. I'll have to do the same:
for (var i = 0; i < length; i++) {
something_here_maybe;
for (var j = 0; j < second_length; j++) {
something_else_here;
}
perhaps_other_thing_here;
}
Now I don't want to keep doing that, so I tried some:
function traverse(before, inside, after) {
for (var i = 0; i < length; i++) {
(before) ? before(i) : null;
for (var j = 0; j < second_length; j++) {
(inside) ? inside(i, j) : null;
}
(after) ? after(i) : null;
}
}
Of course, that seemed much more desirable for me, given that I thought I could do something like:
traverse(function(x) { blabla; }, function(x, y) { blabla; }, function(x) { blabla; });
Mbut ... I simply got to the point where those three functions need to interact with one another. And the variables in them are local - so they can't interact. I'd need to define those variables in traverse(), but I don't know beforehand what variables I'll need. I'd try to define another "initialize" parameter in traverse (as the first argument) which would be a function that initializes those values. But it would still be a function, and those variables would still be local to it, not taken by traverse();
Could you help me with any ideas about this approach ? Or it simply can't be done ? Any idea or advice would be much appreciated. Thank you in advance.
You could use inner functions to accomplish what you are describing, so, if the function traverse takes a parameter based on which the functions A, B and C get defined, define them by writing a function inside the traverse function which returns the three functions as it's return value, (you can return an array with the three functions in them) and then invoke the three functions in your loop.
example:
function traverse(param) {
function defineProcedures(args) {
/* local vars which have function scope(visible to the entire defineProcedures
body
*/
var funcA = function(params) { //blah };
var funcB = function(params) { //blah };
var funcC = function(params) { //blah };
return [funcA, funcB, funcC];
}
var procs = defineProcedures(param);
var firstFunc = procs[0],
secondFunc = procs[1],
thirdFunc = procs[2];
//for loops go here and invoke the functions appropriately.
}
I'm adding an event listener to some elements I'm looping through and need a closure in order to preserve the index in the event function.
<button>solution 1</button>
<button>solution 2</button>
<script>
var buttons = document.getElementsByTagName('button');
for (var i = 0; i < 3; i++) {
var log = (function closure(number) {
return function () {
console.log(number);
};
})(i);
buttons[0].addEventListener("click", log);
}
for (var i = 0, len = 3; i < len; i++) {
(function (i) {
var log = function () {
console.log(i);
};
buttons[1].addEventListener("click", log);
})(i);
}
</script>
http://jsfiddle.net/paptd/11/
Both these solutions output 0, 1, 2 correctly (try 'wrong' to see what happens without a closure) but I'm trying to understand which one I should use and why.
Which way is the correct way of doing it?
The first one works because you are defining a closure, returning a function from it, then assigning that function to a listener.
The second one seems more proper, since the closure encompasses the entire loop content, making it more obvious that the value of i is to be "locked" there.
You shouldn't use any of these--you're creating n identical functions inside of your loop. You should refactor your code into a named function that returns the event handler:
var buttons = document.getElementsByTagName('button');
function createHandler(number) {
return function () {
console.log(number);
};
}
for (var i = 0; i < 3; i++) {
buttons[0].addEventListener("click", createHandler(i));
}
Example: http://jsfiddle.net/paptd/12/
I have a helper function which allows me to call functions in a different context. It's pretty simple:
function delegate(that, thatMethod)
{
return function() { return thatMethod.apply(that,arguments); }
}
This is ok if I wan't evaluate the variables at execution of the function, but sometimes I want to give the delegate-function values which are fixed at construction time.
Sample:
var callbacks = new Array();
for(var i = 0; i < 5; i++)
{
callbacks.push(delegate(window, function() { alert(i) }));
}
callbacks[3]();
In this case my expected behavior is that I get an alert(3) but because i is evaluated at execution we don't.
I know there is another delegate function which looks something like:
function delegatedd( that, thatMethod )
{
if(arguments.length > 2)
{
var _params = [];
for(var n = 2; n < arguments.length; ++n)
_params.push(arguments[n]);
return function() { return thatMethod.apply(that,_params); }
}
else
return function() { return thatMethod.call(that); }
}
But that doesn't help me either because I want to mix both methods. It can be written like that (first version of delegate used):
function(foo) {
return delegate(window, function() {
alert(foo);
});
}(i)
So i is construction time and everything else execution time.
The disadvatage of this is that it looks pretty ugly. Is there a better way to do it? Can I somehow hide it in a function?
Thanks
You can use the bind function:
var callbacks = new Array();
for(var i = 0; i < 5; i++)
{
//callbacks.push(delegate(window, function() { alert(i) }));
callbacks.push(function(n) { alert(n) }.bind(window, i);
}
callbacks[3]();
But bind is not implemented on IE(don't know about IE9), for how get it to work on IE see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind#Compatibility.