I've been using $.each to do iterations for a while now, but I keep on hearing people say to use native JS for to do loops. I'm very concerned about performance but I am not sure if it's always possible to replace $.each with for in a meaningful way.
So my questions are, is it possible to always replace $.each with for, and if not what's the rule of thumb for when it can be done and when it cannot.
I have an each like this:
$this.find("div.class").each(function (){
var $thisparent = $(this).parent();
if (condition) {
$(this).prepend($thisparent.text());
}
if (condition2) {
$(this).prepend($thisparent.text());
}
});
This is what jQuery does with .each, basically:
$.fn.each = function(callback) {
var i, length = this.length;
for(i = 0; i < length; ++i) {
callback.call(this[i]);
}
};
So it's not hard to substitute your anonymous function's 'contents' with the callback.call call. Just be sure to replace this with a temporary with the jQuery object.
Converting your supplied code:
var foo = $this.find("div.class"),
fooLength = foo.length,
i,
$thisparent;
for (i = 0; i < fooLength; ++i) {
$thisparent = $(foo[i]).parent();
if (condition) {
$(foo[i]).prepend($thisparent.text());
}
if (condition2) {
$(foo[i]).prepend($thisparent.text());
}
}
For additional (potential) speed, cache foo[i] into a temporary. too, and assign $thisparent only when needed. If condition and condition2 are mutually exclusive, use a single if (condition || condition2).
Someone compared the performance of for and $.each:
http://jquery-howto.blogspot.com/2009/06/javascript-for-loop-vs-jquery-each.html
Related
I wanted to reformat below code in order to keep as minimum as possible Any suggestion to re-format below code and use it as one single method.
function Cookie_Exist(cookieName) {
var all_cookies = document.cookie.split(';');
for (i = 0; i < all_cookies.length; i++) {
var temp_cookie = all_cookies[i].split('=');
var cookie_name = temp_cookie[0].replace(/^\s+|\s+$/g, '');
if (cookie_name === cookieName) {
return true;
}
}
return false;
}
function Get_Cookie(cookieName) {
var all_cookies = document.cookie.split(';');
for (i = 0; i < all_cookies.length; i++) {
var temp_cookie = all_cookies[i].split('=');
var cookie_name = temp_cookie[0].replace(/^\s+|\s+$/g, '');
if (cookie_name === cookieName) {
return temp_cookie[1];
}
}
return null;
}
Instead of reparsing the cookies everytime one could do that once and build up a Map:
const cookies = new Map(document.cookie.split(";").map(pair => pair.split("=")));
Then its as simple as
cookies.get("name");
or
cookies.has("name")
If you had to keep the function Cookie_Exist rather than #Jonas's method, you would do well to use the array methods rather than a for loop. The abstraction can make code shorter and clearer:
function Cookie_Exist(cookieName) {
const allCookies = document.cookie.split(';');
return allCookies.includes(cookieStr => {
const thisCookieName = cookieStr.split('=')[0].replace(/^\s+|\s+$/g, '');
return cookieName === thisCookieName;
});
}
You don't need the Cookie_Exist() function at all, it does the same as the Get_Cookie() function. Use the Get_Cookie() everywhere instead of Cookie_Exist() and check your result. If it is false, then the cookie does not exist. As simple as that.
You may want to clarify what you mean by 'as minimum as possible.' If you're referring to being efficient, you definitely should go the route suggested by Jonas W.
If you're referring to reducing line count? His answer also is a good one, but I would recommend not focusing too much on line counts. Readability should be your number one goal. In other words, what you should be trying to keep to a minimum is the number of mental hoops a human reader of your code must jump through to understand what is going on.
Also, the regex you're doing to strip whitespace can be replaced with String.prototype.trim() which exists for this purpose.
function parseCookie(cookie) {
let { name, value } = cookie.split('=');
return [ name.trim(), value ];
}
function parseCookies() {
let cookies = document.cookie.split(';')
return new Map(cookies.map(parseCookie))
}
// Usage:
const cookieMap = parseCookies();
cookieMap.has('name');
cookieMap.get('name'):
I am trying to make two arrays. the unique array can get the elements (no repeats) from the text array, and the counter one can count the frequency of each elements. but something is wrong with the counter one.
var unique_array=new Array();
var counter_array=new Array();
var unique=true;
for (i=0;i<text_array.length;i++){
if (unique_array.length==0){
unique_array.push(text_array[0]);
counter_array.push(1);
}
else if(unique_array.length>0&&unique_array.length<=text_array.length){
for (j=0; j<unique_array.length;j++){
if (text_array[i]==unique_array[j]){
counter_array[j]=counter_array[j]+1;// something wrong with the
alert(counter_array[j]);
var unique=false;
}
}
if (unique==true){
unique_array.push(text_array[i]);
counter_array.push[1];
}
unique=true;
}
You could also simplify the code down using a hashmap and some ES5 higher-order functions:
var text_array = ["a1","a1","a2","a3","a2","a4","a1","a5"];
var counts = {};
text_array.forEach(function(el) {
counts[el] = counts.hasOwnProperty(el) ? counts[el]+1 : 1;
});
var unique_array = Object.keys(counts);
var counter_array=unique_array.map(function(key) { return counts[key]; })
You can do this much more simply using an object. Let the values be the keys of an object, then just increment the count of each property as you go. At the end, you can get an array of the unique keys and their values:
var text_array = ['foo','bar','foo','fum','fum','foo'];
var i = text_array.length;
var obj = {};
while (i--) {
if (obj.hasOwnProperty(text_array[i])) {
obj[text_array[i]]++;
} else {
obj[text_array[i]] = 1;
}
}
console.log('Unique values: ' + Object.keys(obj)); // Unique values: foo,fum,bar
console.log('Value counts: ' + Object.keys(obj).map(function(v){return obj[v]})); // Value counts: 3,2,1
Note that the sorting of counts in the output is purely coincidental.
As Jasvir posted, you can make it pretty concise:
var obj = {};
text_array.forEach(function(v) {
obj.hasOwnProperty(v)? ++obj[v] : obj[v] = 1;
});
But the first example is a bit easier to digest.
I think the approach is what's making it difficult. A hash table / associative array would be much easier to work with.
With a hash table (an object {} in JS), you can store each word in a key and increment the value of the key when you encounter the word again. Then, at the end, just go through the hash table and gather up all the keys which have small values. Those are your unique words.
function get_unique_words(text_array) {
var hash_table, i, unique_words, keys;
hash_table = {};
for(i = 0; i < text_array.length; i++) {
if(hash_table[text_array[i]] === undefined) {
hash_table[text_array[i]] = 1;
} else {
hash_table[text_array[i]]++;
}
}
// go through the hash table and get all the unique words
unique_words = [];
keys = Object.keys(hash_table);
for(i = 0; i < keys.length; i++) {
if(hash_table[keys[i]] === 1) {
unique_words.push(keys[i]);
}
}
return unique_words.sort();
}
console.log(get_unique_words(
['blah', 'blah', 'blah', 'goose', 'duck',
'mountain', 'rock', 'paper', 'rock', 'scissors']
));
Some issues and suggestions :
Don't use var twice for the same variable.
Browsers deal with it ok, but for clarity you should only be declaring your variables once.
Always localize your loop counters - forgetting a var before your i and j will cause them to become global variables.
This is relevant when you have a page with lots of code - all global variables will show up in the debugger's watch list at all times, making it harder to debug your code.)
Use the array literal notation [] instead of the function form Array.
The function form is longer and it's easier to forget the new. It's also easier to read (IMO).
Use more whitespace (it won't bite), such as before and after an equals sign:
var x = 1;
// vs.
var x=1;
It makes the code easier to read and most people don't overdo it.
Indent your code when it's inside a block (e.g. function, if, else, while, for, etc.).
This makes it easier to read the control flow of the code and will help prevent bugs.
Use three equals signs (===) unless you are using loose equality on purpose.
This will help someone looking at your code later (probably yourself) understand better what the test is supposed to be testing.
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 9 years ago.
Improve this question
JavaScript only has function scope. Thus, variables declared in for loops are visible for the entire function.
For example,
function foo() {
for(var i = 0; i < n; i++) {
// Do something
}
// i is still in scope here
}
When we have multiple for-loops, this opens the question of how we handle the variables in these other for loops.
Do we use a different variable?
for(var i = 0; i < n; i++) { }
for(var j = 0; j < n; j++) { }
Or do we use the same variable but just assign a value (instead of declaring it)?
for(var i = 0; i < n; i++) { }
for(i = 0; i < n; i++) { }
Or declare i outside of the loops?
var i;
for(i = 0; i < n; i++) { }
for(i = 0; i < n; i++) { }
Or redeclare i?
for(var i = 0; i < n; i++) { }
for(var i = 0; i < n; i++) { }
All of these work (or at least they do on the latest versions of my browsers). JSHint doesn't like the last approach, though.
Is there an approach which is most idiomatic or otherwise preferable?
It really depends on who you're coding for. If you're coding for a company or contributing to a library you of course follow their style guide. I've seen all of these (expect the last) used in libraries. If you like the Douglas Crockford style you'll go with the second to last and place all your variables at the top of function scope (or jslint will shout at you).
Taking an example from the jQuery style guide:
This is considered Good style
var i = 0;
if ( condition ) {
doSomething();
}
while ( !condition ) {
iterating++;
}
for ( ; i < 100; i++ ) {
object[ array[ i ] ] = someFn( i );
}
While this is poor style:
// Bad
if(condition) doSomething();
while(!condition) iterating++;
for(var i=0;i<100;i++) object[array[i]] = someFn(i);
Anyway, because this is style I'm going to reference how several libraries write their for each loops:
jQuery uses the Crockford style in core as does lodash
Underscore js uses the last style as seen here. Mootools also uses this style
If your code is going to be minimized before you release it, it will not matter as minifiers will mangle it to pretty much the same end representation in the processing.
Using different variables we have no problems.
Reusing and reassigning makes the code less readable, and if we remove the declaration at a later time, we risk assigning i to something outside of the function scope.
Declaring i outside the loops, we have no problems.
Redeclaring will be an issue if your lint tool, IDE, etc complain.
So I would argue for the first or third option. If number of variables is a concern using the first option, then you are may be in need of a refactoring.
Another take that answers the question in a different way.
A function with multiple loops makes me suspicious because:
It may be doing too much and should be decomposed anyway, and
It may be better to write it more functionally and eliminate the index altogether (it's available in some each-/map-y functions anyway)
Another approach is to use iterator functions. For example, in modern browsers an Array will have a forEach method:
var items = ["one", "two", "three"];
var things = ["hello", "goodbye"];
items.forEach(function (item, index) {
// Do stuff
});
things.forEach(function (item, index) {
// Do stuff
});
If you're using older browsers (or custom collections), you can make your own iterator like this:
Array.prototype.forEach = function(callback) {
for(var i = 0; i < this.length; i++) {
callback.apply(this, [this[i], i, this]);
}
};
For more information see: Array.prototype.forEach()
Any variables declared within a function are interpreted as being declared at the beginning of the function. Doug Crockford argues that you should declare all of your variables at the first line of every function.
doSomething = function() {
var i, ... other variables ...;
...
for (i = 0; i < x; i += 1) {
...
}
...
for (i = 0; i < x; i += 1) {
...
}
}
This way the code reads in the same way it will be parsed by the javascript engine.
I need to Loop in JQuery from 0 to variable-value(dynamically entered by user).How can i achieve this?
Now i am doing it by using simple For loop like this.
for( i=1; i<=fetch; i++) {
var dyndivtext = document.createElement("input");
document.body.appendChild(dyndivtext);
}
Thanks.
You could loop an empty array:
$.each(new Array(fetch), function(i) {
var dyndivtext = document.createElement("input");
document.body.appendChild(dyndivtext);
});
If you do this alot you can even fake-patch jQuery.each to take numbers:
(function($) {
var _each = $.each;
$.each = function() {
var args = $.makeArray(arguments);
if ( args.length == 2 && typeof args[0] == 'number') {
return _each.call(this, new Array(args[0]), args[1]);
}
return _each.call(this, args);
};
}(jQuery));
$.each(fetch, function(i) {
// loop
});
jQuery.each does have some great features, like the different return values inside the callback. But for a simple loop I find it much more convenient (and less overhead) to do something like:
while(fetch--) {
// loop
}
To loop between two values you should use a regular Javascript loop. The jQuery each methods are used when looping through a collection of elements or an array.
To loop from zero, you should initialise the loop variable to zero, not one. To loop from zero to the specified value, you use the <= for the comparison, but to loop from zero and the number of items as specified (i.e. from 0 to value-1), you use the < operator.
for (i = 0; i < fetch; i++) {
$('body').append($('<input/>', { type: 'text' }));
}
You mean Javascript loop.
From W3Schools:
for (var variable = startvalue; variable < endvalue; variable = variable + increment)
{
//code to be executed
}
To get the value from user and run the code you can use the following prompt.
var x=prompt("Enter the value",0);
for(i=0;i<x;i++)
{
var dyndivtext = document.createElement("input");
document.body.appendChild(dyndivtext);
}
Hope this helps.
Thanks
If you want it the full jQuery way then use that new plugin jQuery-timing. It provides inline-loops in your jQuery line:
$('body').repeat().append('<input>').until(fetch);
Nice, eh?
I've been writing a javascript function which returns true if the value matches one of about 4 values (just 3 in the example below). The problem is, when I have just two values the function works correctly, but adding a third breaks the code.
I'm pretty new to javascript and I'm guessing there's a much better way of doing this? I've tried searching but found nothing as of yet.
Any help is much appreciated.
function isValid(elem, helperMsg){
var sn6 = /[sS][nN]6/;
var sn5 = /[sS][nN]5/;
var sn38 = /[sS][nN]38/;
if(elem.value.match(sn6 || sn5 || sn38)){
//do stuff
return true;
}else{
return false;
}
}
Edit:
Here's my second attempt with an array:
function isLocal(elem, helperMsg){
var validPostcodes=new Array();
validPostcodes[0]= /[wW][rR]12/;
validPostcodes[1]= /[cC][vV]35/;
validPostcodes[2]= /[sS][nN]99/;
validPostcodes[3]= /[sS][nN]6/;
validPostcodes[4]= /[sS][nN]5/;
validPostcodes[5]= /[sS][nN]38/;
validPostcodes[6]= /[oO][xX]29/;
validPostcodes[7]= /[oO][xX]28/;
var i = 0;
for (i = 0; i < validPostcodes.length; ++i) {
if(elem.value.match(validPostcodes[i])){
// do stuff
return true;
}else{
alert(helperMsg);
elem.focus();
return false;
}
}
}
a || b || c
is an expression that evaluates to a boolean. That means that you're running either match(true) or match(false). You must write it as:
match(a) || match(b) || match(c)
Another option would be to store them in an array and loop over it. That would mean if the number of patterns grew you wouldn't have to change code other than the list of patterns. Another approach, though limited to this situation, might be to change the pattern to one that is equivalent to or-ing the three options together (untested, and I'm a bit rusty on regex):
elem.value.match(/[sSnN][6|5|38]/)
Array based example:
var patterns = [/../, /.../];
for (var i = 0; i < patterns.length; ++i) {
if (elem.value.match(patterns[i])) { return true; }
}
In real code, I would probably format it like this:
function isValid(elem, helperMsg){
var patterns = [/../, /.../],
i = 0;
for (i = 0; i < patterns.length; ++i) {
if (elem.value.match(patterns[i])) {
return true;
}
}
}
That's just a habit though since JavaScript hoists variables to the top of their scope. It's by no means required to declare the variables like that.