I was reading John Resig's Learning Advanced JavaScript slides.
As i came to the slide-27, john presents a quiz as per below :
QUIZ: How can we implement looping with a callback?
function loop(array, fn){
for ( var i = 0; i < array.length; i++ ) {
// Implement me!
}
}
var num = 0;
loop([0, 1, 2], function(value){
assert(value == num++, "Make sure the contents are as we expect it.");
assert(this instanceof Array, "The context should be the full array.");
});
I tried to implement, and came up with following code :
function loop(array, fn){
for ( var i = 0; i < array.length; i++ ) {
fn.call(array, array[i]);
}
}
var num = 0;
loop([0, 1, 2], function(value){
assert(value == num++, "Make sure the contents are as we expect it.");
assert(this instanceof Array, "The context should be the full array.");
});
I was happy that it worked, and eager to see the next slide to compare it with solution john will provide in next slide.
but in next slide john provided the following solution :
function loop(array, fn){
for ( var i = 0; i < array.length; i++ )
fn.call( array, array[i], i );
}
var num = 0;
loop([0, 1, 2], function(value, i){
assert(value == num++, "Make sure the contents are as we expect it.");
assert(this instanceof Array, "The context should be the full array.");
});
to the fn function he passed to loop(), he added another parameter i.
that makes me wonder why another parameter is required ??
In a normal for loop, the body has access to the index i. If the callback solution is intended as a replacement for this, it should also have this information available. It could be useful for creating unique identifiers, for instance. So we pass it as a parameter.
Related
I am a beginner in code world. I have troubles understanding recursion in JavaScript especially when it needs two or more looping. Like I want to print rectangle using recursion. I don't know completely how to make a base case, condition when it still executed. For examples, these codes below I use to print rectangle or holey rectangle.
function box(num) {
for (let i = 0; i < num; i++) {
let str = ''
for (let j = 0; j < num; j++) {
str += '*'
}
console.log(str)
}
}
box(5)
function holeBox (num) {
for(let i = 0; i < num; i++){
let str = ''
for(let j = 0; j < num; j++){
if(i == 0 || i == num -1 || j == 0 || j == num - 1) {
str += '*'
} else {
str += ' '
}
}
console.log(str)
}
}
holeBox (5)
Please help me to understand recursion, an explanation would be great. My goals are not only to solve those codes but also to understand how recursion works. I've searched there's no good source to learn recursion, or I just too dumb to understand. Thanks in advance
To understand how recursion works, just think of how you can split up what you want to accomplish into smaller tasks, and how the function can complete one of those tasks, and then call itself to do the next- and so on until it is finished. I personally don't think printing boxes is the best way to learn recursion, so imagine you wanted to search an array for a specific value; ignore JavaScript's indexOf()/find() functions or similar for now.
To do this using loops, its easy, just iterate over the array, and check every value:
//Returns the index of the first occurrence of a value in an array, or -1 if nothing is found
function search(needle, haystack) {
for (let i = 0; i < haystack.length; i++) {
if (haystack[i] == needle) return i;
}
return -1;
}
Doing this using recursion is easy as well:
function recursiveSearch(needle, haystack, i) {
if (i > (haystack.length - 1)) return -1; //check if we are at the end of the array
if (haystack[i] == needle) return i; //check if we've found what we're looking for
//if we haven't found the value yet and we're not at the end of the array, call this function to look at the next element
return recursiveSearch(needle, haystack, i + 1);
}
These functions do the same thing, just differently. In the recursive function, the two if statements are the base cases. The function:
Tests if the current element is out of bounds of the array (meaning we've already searched every element), and if so, returns -1
Tests if the current element is what we're looking for, and if so, returns the index
If neither of the statements above apply, we call this function recursively to check the next element
Repeat this until one of the base cases kicks in.
Note that recursive functions are usually called from other helper functions so that you don't have to pass the initial parameters to call the function. For example, the recursiveSearch() function above would be private, and it would be called by another function like this:
function search(needle, haystack) {
return recursiveSearch(needle, haystack, 0);
}
so that we don't have to include the third parameter when we call it, thus decreasing confusion.
Yes, even your box code can be turn into recursion but I don't think it will help you understand the concept of recursion.
If you really have to:
function getBox(arr, size) {
let length = arr.length;
if (length == size)
return arr; // recursion stop rule - if the size reached
for (let i = 0; i < length; i++)
arr[i].push("*"); // fill new size for all row
arr.push(new Array(length + 1).fill("*")); // add new row
return getBox(arr, size); // recursive call with arr bigger in 1 row
}
However, I believe #Gumbo answer explain the concept better then this...
The snippet below is from MDN - A reintroduction to Javascript, it is supposed to demonstrate IIFE. I kinda see that it is supposed to count the characters in this text node but I am not sure about a couple things. The first is why does the for statement have 2 arguments in the first argument section var i=0, child. The second is more general, how does it work with this function calling itself .. can someone explain the overall flow to me please?
var charsInBody = (function counter(elm) {
if (elm.nodeType == 3) { // TEXT_NODE
return elm.nodeValue.length;
}
var count = 0;
for (var i = 0, child; child = elm.childNodes[i]; i++) {
count += counter(child);
}
return count;
})(document.body);
The first is why does the for statement have 2 arguments in the first argument section var i=0,child ?
A for loop is just a condensed version of a while loop, that means that:
for(declarations; condition; last) {
body
}
is the same as:
declarations
while(condition) {
body
last
}
That means that in your case it is as:
var i = 0, child;
while(child = elm.childNodes[i]) {
count += counter(child);
i++
}
So actually child just defines a new variable before the loop
There's a gap in my JavaScript knowledge here. I want to search an array of objects values for a particular value and return it.
For the year I have been writing JavaScript, I have been implementing it like this:
var itemClicked = (function(){
var retval;
//Note self.inventory.itemsArray is an array of JS objects
$(self.inventory.itemsArray).each(function(i){
if(parseInt(this.id) === parseInt(idOfItem)){
retval = this;
return false;
}
});
return retval;
})();
It works, but I'm sure as anything there is a more elegant way. Tell me please!
EDIT - Solution
Thanks to #gdoron with his answer below.
var myVar = $(self.owner.itemsArray).filter(function(){
return parseInt(this.id) == parseInt(recItemID);
}).get(0);
Note: .get(0) was added at the end because myVar is wrapped as a jQuery object.
The native jQuery function for this is filter:
$(data).filter(function(){
return this.id == "foo";
});
It's shorter than code you have and more important a lot more readable.
About efficiency, it will iterate all the elements in the set to find as much as possible matches, but I hardly believe it will be the bottle neck of your application, don't focus on micro-optimisations.
I suggest you read Eric Lipper blog about Which is faster.
You can also use grep as suggested by #Mattias Buelens:
$.grep(data, function(ele){
retun ele.id == "foo";
});
Just another option using jQuery's $.grep( ) function
var arr = $.grep( self.inventory.itemsArray, function ( n ) {
return n.id == idOfItem;
});
The above returns an array of matching array elements. If you just want the first it is easy enough to return arr[0] if it exists.
Although I'm unsure what the function is actually supposed to do (due to the external contexts' variables), the following should be more efficient cycle-wise
var itemClicked = (function(){
var i, array = self.inventory.itemsArray, length = array.length;
for( i=0; i < length; i++) {
if(parseInt(array[i].id) === parseInt(idOfItem)){
return array[i];
}
}
return undefined;
})();
It's an array of Javascript objects
Then do not use jQuery at all. At least, use $.each instead of building a wrapper object around the array. Still, a simple for-loop is much shorter and more performant:
var itemClicked = (function(idnum) {
var arr = self.inventory.itemsArray;
for (var i=0, l=arr.length; i<l; i++)
if (parseInt(arr[i].id, 10) === idnum)
return arr[i];
})( parseInt(idOfItem, 10) );
You might as well think of storing the id properties as numbers right away, so you don't need to convert it each time.
I'm trying to set up an IF statement if a value is contained within an array.
I've found some code which claimed to work but it doesn't seem to be.
var myAsi = ['01','02','24OR01','30De01','9thC01','A.Hu01','A01','AACAMSTE','ABBo01','ABBo02','ABC-01','ACCE01','Acce02','AceR01','h+dm01','Merr02','Ofak01','Wage01','Youn01'];
Array.prototype.find = function(searchStr) {
var returnArray = false;
for (i=0; i<this.length; i++) {
if (typeof(searchStr) == 'function') {
if (searchStr.test(this[i])) {
if (!returnArray) { returnArray = [] }
returnArray.push(i);
}
} else {
if (this[i]===searchStr) {
if (!returnArray) { returnArray = [] }
returnArray.push(i);
}
}
}
return returnArray;
}
var resultHtml = '';
resultHtml+='<table style ="width: 400px">';
resultHtml+='<tr colspan="2">';
resultHtml+='<td colspan="2">';
resultHtml+='<b><font color = "Red">(Client Code)</font><br><font color = "green">(Company Name)</font></b>';
resultHtml+='</td>';
resultHtml+='</tr>';
$.each(data, function(i,item){
resultHtml+='<div class="result">';
resultHtml+='<tr>';
if (notFound=myAsi.find("'"+item.code+"'") == false) {
resultHtml+='<td>';
}
else {
resultHtml+='<td bgcolor=#D8D8D8>';
}
resultHtml+='<font color = "red">'+item.code+'</font><br>';
resultHtml+='<font color = "green">'+item.content+'</font></td>';
resultHtml+='<td style ="width: 80px">Remove - ';
resultHtml+='Add';
resultHtml+='</td>';
resultHtml+='</tr>';
resultHtml+='</div>';
});
resultHtml+='</table>';
The item.code cycles through and I need an IF statement to tell me if it appears within the array.
Any help would be great.
If you only want to find if an item is in an array you could use a simpler function than that. For eg. the jQuery implementation:
// returns index of the element or -1 if element not present
function( elem, array ) {
if ( array.indexOf ) {
return array.indexOf( elem );
}
for ( var i = 0, length = array.length; i < length; i++ ) {
if ( array[ i ] === elem ) {
return i;
}
}
return -1;
},
This uses the native browser implementation of indexOf if available (all browsers except IE I think), otherwise a manual loop.
Try removing the apostrophes from your find() call. eg
notFound=myAsi.find(item.code)
Though actually, for your purposes see this example which uses this function....
Array.prototype.find = function(searchStr) {
for (var i=0; i<this.length; i++) {
if (this[i]==searchStr) return true;
};
return false;
};
And as an aside - Be very careful about using var before using a variable - otherwise you create a global variable (which you probably don't want). ie the line in your original function....
for (i=0; i<this.length; i++)
i is now global...
Array.prototype.contains = function(value, matcher) {
if (!matcher || typeof matcher !== 'function') {
matcher = function(item) {
return item == value;
}
}
for (var i = 0, len = this.length; i < len; i++) {
if (matcher(this[i])) {
return true;
}
}
return false;
};
This returns true for elements in the array that statisfy the conditions defined in matcher. Implement like this:
var arr = ['abc', 'def', 'ghi']; // the array
var valueToFind= 'xyz'; // a value to find in the array
// a function that compares an array item to match
var matcher = function(item) {
return item === matchThis;
};
// is the value found?
if (arr.contains(valueToFind, matcher)) {
// item found
} else {
// item not found
}
UPDATES:
Changed the contains method to take a value and an optional matcher function. If no matcher is included, it will do a simple equality check.
Test this on jsFiddle.net: http://jsfiddle.net/silkster/wgkru/3/
You could just use the builtin function
['a','b','c'].indexOf('d') == -1
This behavior was mandated in the javascript specification from over 6 years ago. Though I gave up on Internet Explorer for these reasons at around IE8, because of this incredibly poor support for standards. If you care about supporting very old browsers, you can use http://soledadpenades.com/2007/05/17/arrayindexof-in-internet-explorer/ to tack on your own custom Array.indexOf
I don't recall IE9 supporting [].indexOf, but Microsoft claims it does: http://msdn.microsoft.com/en-us/library/ff679977(v=VS.94).aspx
The standard way to determine the index of the first occurence of a given value in an array is the indexOf method of Array objects.
This code checks if it this method is supported, and implements it if not, so that it is available on any Array object:
if(Array.prototype.indexOf==null)
Array.prototype.indexOf = function(x){
for(var i=0, n=this.length; i<n; i++)if(this[i]===x)return i;
return -1;
};
Now myArray.indexOf(myValue) returns the first index of myValue in myArray, or -1 if not found.
I just saw a video of Nicholas Zakas of Yahoo, at GoogleTalks talking about speeding up your website. One of the things he mentioned was doing loops in reverse order to skip one of two comparisons: for (i = len; i--;) {}
And he said to keep away from JS libraries implementations of for each. Just for fun I thought I'd try it out. Turns out he was wrong.
var array1 = new Array();
var array2 = new Array();
var start = 0;
var finished = 0;
start = (new Date).getTime();
$("#newDivTest").children().each(function(i){
array1[i] = $(this).get(0).id;
});
finished = (new Date).getTime() - start;
alert(finished);
start = (new Date).getTime();
var len = $("#newDivTest").children().length;
for (i = len; i--;) {
array2[i] = $(this).get(0).id;
}
finished = (new Date).getTime() - start;
alert(finished);
newDivTest holds 1000 empty divs with an id starting at "0" and going up to "999". Another note is that $(this).get(0).id is about 3 times faster than $(this).attr("id") for some reason, anyone know why?
For FF3.5, the results are "7" and "45", IE7 gives "30" and "45", Chrome2 gives "4" and "17", Opera10 gives "16" and "16", and lastly Safari4 gives "4" and "16".
So it seems the approach Nicholas is hardest against is actually the faster in almost all instances.
I'm not smart enough to know what's going on behind the scenes for jQuery's each()-method, but it must be doing something right...right?
One flaw in your setup is that your second test will not actually work. You wrote:
for (i = len; i--;) {
array2[i] = $(this).get(0).id;
}
But this is not defined there, so the entire operation will fail. You'd have to do something like:
var children = $("#newDivTest").children();
for (i = children.length; i--;) {
array2[i] = children.get(i).id;
}
And this gets at a more pressing issue than performance: although calls to something like jQuery's .each() function do result in added function calls (and the associated added overhead), they also tend to make it much easier to express what you want the code to do.
Quoting Michael Jackson: "The First Rule of Program Optimization: Don't do it. The Second Rule of Program Optimization (for experts only!): Don't do it yet."
Aren't your tests doing different things?
In the second test this is not the same as the first one.
Slightly off topic and not a direct answer to your main question but, jQuery's each method is implemented like so (jQuery 1.3.2)
jQuery.extend({
/* ... Code taken out for brevity ... */
// args is for internal usage only
each: function( object, callback, args ) {
var name, i = 0, length = object.length;
if ( args ) {
if ( length === undefined ) {
for ( name in object )
if ( callback.apply( object[ name ], args ) === false )
break;
} else
for ( ; i < length; )
if ( callback.apply( object[ i++ ], args ) === false )
break;
// A special, fast, case for the most common use of each
} else {
if ( length === undefined ) {
for ( name in object )
if ( callback.call( object[ name ], name, object[ name ] ) === false )
break;
} else
for ( var value = object[0];
i < length && callback.call( value, i, value ) !== false; value = object[++i] ){}
}
return object;
}
/* ... Code taken out for brevity ... */
);
as you can see, a callback function is applied to each property of object. the jQuery object has a length property defined so will perform the following loop (generally, no args are supplied)
for ( var value = object[0]; i < length && callback.call( value, i, value ) !== false; value = object[++i] ){}
in each iteration, the callback function will increase the scope chain length by 1, thus will take longer to resolve the reference to the object's property.
I notice that your question is "JS looping and populating array. Which is faster?", but your examples are actually testing the speed of various selectors of JQuery, right? You might be interested in checking out :http://mootools.net/slickspeed/
As for "JS looping and populating array. Which is faster?", see here : http://blogs.oracle.com/greimer/resource/loop-test.html