Alternatives to javascript function-based iteration (e.g. jQuery.each()) - javascript

I've been watching Google Tech Talks' Speed Up Your Javascript and in talking about loops, the speaker mentions to stay away from function-based iterations such as jQuery.each() (among others, at about 24:05 in the video). He briefly explains why to avoid them which makes sense, but admittedly I don't quite understand what an alternative would be. Say, in the case I want to iterate through a column of table cells and use the value to manipulate the adjacent cell's value (just a quick example). Can anyone explain and give an example of an alternative to function-based iteration?

Just a simple for loop should be quicker if you need to loop.
var l = collection.length;
for (var i = 0; i<l; i++) {
//do stuff
}
But, just because it's quicker doesn't mean it's always important that it is so.
This runs at the client, not the server, so you don't need to worry about scaling with the number of users, and if it's quick with a .each(), then leave it. But, if that's slow, a for loop could speed it up.

Ye olde for-loop

It seems to me that it would be case that function-based iteration would be slightly slower because of the 1) the overhead of function itself, 2) the overhead of the callback function being created and executed N times, and 3) the extra depth in the scope chain. However, I thought I'd do a quick benchmark just for kicks. Turns out, at least in my simple test-case, that function-based iteration was faster. Here's the code and the findings
Test benchmark code
// Function based iteration method
var forEach = function(_a, callback) {
for ( var _i=0; _i<_a.length; _i++ ) {
callback(_a[_i], _i);
}
}
// Generate a big ass array with numbers 0..N
var a = [], LENGTH = 1024 * 10;
for ( var i=0; i<LENGTH; i++ ) { a.push(i); }
console.log("Array length: %d", LENGTH);
// Test 1: function-based iteration
console.info("function-base iteration");
var end1 = 0, start1 = new Date().getTime();
var sum1 = 0;
forEach(a, function(value, index) { sum1 += value; });
end1 = new Date().getTime();
console.log("Time: %sms; Sum: %d", end1 - start1, sum1);
// Test 2: normal for-loop iteration
console.info("Normal for-loop");
var end2 = 0, start2 = new Date().getTime();
var sum2 = 0;
for (var j=0; j<a.length; j++) { sum2 += a[j]; }
end2 = new Date().getTime();
console.log("Time: %sms; Sum: %d", end2 - start2, sum2);
Each test just sums the array which is simplistic, but something that can be realistically seen in some sort of real life scenario.
Results for FF 3.5
Array length: 10240
function-base iteration
Time: 9ms; Sum: 52423680
Normal for-loop
Time: 22ms; Sum: 52423680
Turns out that a basic for iteration was faster in this test case. I haven't watched the video yet, but I'll give it a look and see if he's differing somewhere that would make function-based iterations slower.
Edit: This is by no means the end-all, be-all and is only the results of one engine and one test-case. I fully expected the results to be the other way around (function-based iteration being slower), but it is interesting to see how certain browsers have made optimizations (which may or may not be specifically aimed at this style of JavaScript) so that the opposite is true.

The fastest possible way to iterate is to cut down on stuff you do within the loop. Bring stuff out of the iteration and minimise lookups/increments within the loop, e.g.
var i = arr.length;
while (i--) {
console.log("Item no "+i+" is "+arr[i]);
}
NB! By testing on the latest Safari (with WebKit nightly), Chrome and Firefox, you'll find that it really doesn't matter which kind of the loop you're choosing if it's not for each or for in (or even worse, any derived functions built upon them).
Also, what turns out, is that the following for loop is very slightly even faster than the above option:
var l = arr.length;
for (var i=l; i--;) {
console.log("Item no "+i+" is "+arr[i]);
}

If the order of looping doesn't matter, the following should be fastest as you only need a single local variable; also, decrementing the counter and bounds-checking are done with a single statement:
var i = foo.length;
if(i) do { // check for i != 0
// do stuff with `foo[i]`
} while(--i);
What I normally use is the following:
for(var i = foo.length; i--; ) {
// do stuff with `foo[i]`
}
It's potentially slower than the previous version (post- vs pre-decrement, for vs while), but more readable.

Related

Setting variable to existing value versus return?

In Javascript, I have a function that sets a variable. If the function tries to set the variable to its current value, is it more "efficient" to break out of the function, or let the function re-set the variable's value?
Example
var a;
function setStuff(x) {
if (a == x) { return; }
a = x;
}
versus
var a;
function setStuff(x) {
a = x;
}
This function will be called on page scroll, so it will be called at a high frequency.
I don't think the issue is "efficiency".
I do however think there's a practice at play here, which is to generally not manipulate values outside the scope of the function. Having many functions like these in your application will drive you nuts, wondering which function is changing what.
Instead return a new value.
var setStuff = function() {
return newValue;
}
var a = setStuff();
I wrote a simple test snippet:
var a;
function setStuffCheck(x) {
if (a == x) { return; }
a = x;
}
function setStuff(x) {
a = x;
}
function benchmark(func){
var startTime = Date.now();
var callCount = 1000000;
for(var i = 0; i < callCount; i++){
func(10);
}
console.log((Date.now() - startTime) + "ms for "+callCount+" calls setting always the same value");
startTime = Date.now();
for(var i = 0; i < callCount; i++){
func(i);
}
console.log((Date.now() - startTime) + "ms for "+callCount+" calls setting always different values");
}
benchmark(setStuffCheck);
benchmark(setStuff);
By copying and pasting it in the console (Firefox 46.0.1), I have something like this:
138ms for 1000000 calls setting always the same value
216ms for 1000000 calls setting always different values
77ms for 1000000 calls setting always the same value
78ms for 1000000 calls setting always different values
So the second way seems to be always better. But the results may be different on each computers. However, the difference is noticable only for 1 millions of calls (try changing it to 1000, there'll be no differences).
The second option makes more sense to me and is more viable as there is very less logic written as compared to the first one as it is checking whether two values are equal or not , whereas in the second option its just re-assigning the variable to a new value.
if condition is always slower compared to when just assigning a new value . So i think you should go with second option.
There are potentially two factors that will likely drown out any practical performance difference between the two options. In practice, I would suggest that you use the version that is the easiest to understand and explain to others. I think that would probably be the unconditional update but it would be more up to you.
The two things that are likely going to obfuscate any real differences are:
What else are you doing in the function
What effect branch prediction has on your conditional.
Now, specifically to the question of what version is faster. I have set up the following test with each option executed a million times with 10 runs of each test. The global is set to itself 1 in 10 times but you can change that to some other frequency by setting aNew
var a = 10;
var ittr = 1000 * 1000;
function getRandomIntInclusive(min, max) { return Math.floor(Math.random() * (max - min + 1)) + min; }
function set1(x){
if (a === x){ return; }
a = x;
}
function set2(x){
a = x;
}
for (var j = 0; j < 10; j++){
var start = performance.now();
for (var i = 0; i< ittr; i++){
var aNew = a - 10 + getRandomIntInclusive(1,19);
set1(aNew);
}
console.log("conditional : " + (performance.now() - start));
}
for (var j = 0; j < 10; j++){
var start = performance.now();
for (var i = 0; i< ittr; i++){
var aNew = a - 10 + getRandomIntInclusive(1,19);
set2(aNew);
}
console.log("unconditional : " + (performance.now() - start));
}
Your results may vary but the I see conditional set() averages about 18 after settling down. The unconditional about the same, maybe 17.5.
Note that the vast bulk of the time here is taken by the call to random(). If you consistently just set the global to itself both functions time at around 1.8 rather than 18, suggesting that whatever else you might do in your set() is likely to obfuscate any performance difference.
The two do not have necessarily have identical results. Consider this series of calls:
var a;
function setStuff(x) {
if (a == x) { return; }
a = x;
}
setStuffCheck(0) ;
setStuffCheck('0');
console.log(a);
Output is not '0', but 0.
For a good comparison, the setStuffCheck function should use the strict equality operator ===.
In my tests in FireFox I see the performance of both functions show very little difference. setStuffCheck seems to take slightly more time to execute than setStuff when the argument has a different value than a, but it is the opposite (also slightly) when the values are the same. The difference in either way is in the order of 2%, which is the kind of fluctuations you get anyway on a typical device/PC for other causes, that have nothing to do with the code.
Anyway, this also means that this slight performance difference will depend on how often you expect to call the function with an argument that is equal to a.
However, the difference is only noticeable when you would do hundreds of millions of calls. If you don't have that many calls, then don't even bother and choose for setStuff.

Speed differences in JavaScript functions finding the most common element in an array

I'm studying for an interview and have been working through some practice questions. The question is:
Find the most repeated integer in an array.
Here is the function I created and the one they created. They are appropriately named.
var arr = [3, 6, 6, 1, 5, 8, 9, 6, 6]
function mine(arr) {
arr.sort()
var count = 0;
var integer = 0;
var tempCount = 1;
var tempInteger = 0;
var prevInt = null
for (var i = 0; i < arr.length; i++) {
tempInteger = arr[i]
if (i > 0) {
prevInt = arr[i - 1]
}
if (prevInt == arr[i]) {
tempCount += 1
if (tempCount > count) {
count = tempCount
integer = tempInteger
}
} else {
tempCount = 1
}
}
console.log("most repeated is: " + integer)
}
function theirs(a) {
var count = 1,
tempCount;
var popular = a[0];
var temp = 0;
for (var i = 0; i < (a.length - 1); i++) {
temp = a[i];
tempCount = 0;
for (var j = 1; j < a.length; j++) {
if (temp == a[j])
tempCount++;
}
if (tempCount > count) {
popular = temp;
count = tempCount;
}
}
console.log("most repeated is: " + popular)
}
console.time("mine")
mine(arr)
console.timeEnd("mine")
console.time("theirs")
theirs(arr)
console.timeEnd("theirs")
These are the results:
most repeated is: 6
mine: 16.929ms
most repeated is: 6
theirs: 0.760ms
What makes my function slower than their?
My test results
I get the following results when I test (JSFiddle) it for a random array with 50 000 elements:
mine: 28.18 ms
theirs: 5374.69 ms
In other words, your algorithm seems to be much faster. That is expected.
Why is your algorithm faster?
You sort the array first, and then loop through it once. Firefox uses merge sort and Chrome uses a variant of quick sort (according to this question). Both take O(n*log(n)) time on average. Then you loop through the array, taking O(n) time. In total you get O(n*log(n)) + O(n), that can be simplified to just O(n*log(n)).
Their solution, on the other hand, have a nested loop where both the outer and inner loops itterate over all the elements. That should take O(n^2). In other words, it is slower.
Why does your test results differ?
So why does your test results differ from mine? I see a number of possibilities:
You used a to small sample. If you just used the nine numbers in your code, that is definately the case. When you use short arrays in the test, overheads (like running the console.log as suggested by Gundy in comments) dominate the time it takes. This can make the result appear completely random.
neuronaut suggests that it is related to the fact that their code operates on the array that is already sorted by your code. While that is a bad way of testing, I fail to see how it would affect the result.
Browser differences of some kind.
A note on .sort()
A further note: You should not use .sort() for sorting numbers, since it sorts things alphabetically. Instead, use .sort(function(a, b){return a-b}). Read more here.
A further note on the further note: In this particular case, just using .sort() might actually be smarter. Since you do not care about the sorting, only the grouping, it doesnt matter that it sort the numbers wrong. It will still group elements with the same value together. If it is faster without the comparison function (i suspect it is), then it makes sense to sort without one.
An even faster algorithm
You solved the problem in O(n*log(n)), but you can do it in just O(n). The algorithm to do that is quite intuitive. Loop through the array, and keep track of how many times each number appears. Then pick the number that appears the most times.
Lets say there are m different numbers in the array. Looping through the array takes O(n) and finding the max takes O(m). That gives you O(n) + O(m) that simplifies to O(n) since m < n.
This is the code:
function anders(arr) {
//Instead of an array we use an object and properties.
//It works like a dictionary in other languages.
var counts = new Object();
//Count how many of each number there is.
for(var i=0; i<arr.length; i++) {
//Make sure the property is defined.
if(typeof counts[arr[i]] === 'undefined')
counts[arr[i]] = 0;
//Increase the counter.
counts[arr[i]]++;
}
var max; //The number with the largest count.
var max_count = -1; //The largest count.
//Iterate through all of the properties of the counts object
//to find the number with the largerst count.
for (var num in counts) {
if (counts.hasOwnProperty(num)) {
if(counts[num] > max_count) {
max_count = counts[num];
max = num;
}
}
}
//Return the result.
return max;
}
Running this on a random array with 50 000 elements between 0 and 49 takes just 3.99 ms on my computer. In other words, it is the fastest. The backside is that you need O(m) memory to store how many time each number appears.
It looks like this isn't a fair test. When you run your function first, it sorts the array. This means their function ends up using already sorted data but doesn't suffer the time cost of performing the sort. I tried swapping the order in which the tests were run and got nearly identical timings:
console.time("theirs")
theirs(arr)
console.timeEnd("theirs")
console.time("mine")
mine(arr)
console.timeEnd("mine")
most repeated is: 6
theirs: 0.307ms
most repeated is: 6
mine: 0.366ms
Also, if you use two separate arrays you'll see that your function and theirs run in the same amount of time, approximately.
Lastly, see Anders' answer -- it demonstrates that larger data sets reveal your function's O(n*log(n)) + O(n) performance vs their function's O(n^2) performance.
Other answers here already do a great job of explaining why theirs is faster - and also how to optimize yours. Yours is actually better with large datasets (#Anders). I managed to optimize the theirs solution; maybe there's something useful here.
I can get consistently faster results by employing some basic JS micro-optimizations. These optimizations can also be applied to your original function, but I applied them to theirs.
Preincrementing is slightly faster than postincrementing, because the value does not need to be read into memory first
Reverse-while loops are massively faster (on my machine) than anything else I've tried, because JS is translated into opcodes, and guaranteeing >= 0 is very fast. For this test, my computer scored 514,271,438 ops/sec, while the next-fastest scored 198,959,074.
Cache the result of length - for larger arrays, this would make better more noticeably faster than theirs
Code:
function better(a) {
var top = a[0],
count = 0,
i = len = a.length - 1;
while (i--) {
var j = len,
temp = 0;
while (j--) {
if (a[j] == a[i]) ++temp;
}
if (temp > count) {
count = temp;
top = a[i];
}
}
console.log("most repeated is " + top);
}
[fiddle]
It's very similar, if not the same, to theirs, but with the above micro-optimizations.
Here are the results for running each function 500 times. The array is pre-sorted before any function is run, and the sort is removed from mine().
mine: 44.076ms
theirs: 35.473ms
better: 32.016ms

Why is Selection Sort so fast in Javascript?

I'm studying for a technical interview right now, and writing quick javascript implementations of different sorts. The random-array benchmark results for most of the elementary sorts makes sense but the selection sort is freakishly fast. And I don't know why.
Here is my implementation of the Selection Sort:
Array.prototype.selectionSort = function () {
for (var target = 0; target < this.length - 1; target++) {
var min = target;
for (var j = target + 1; j < this.length - 1; j++) {
if (this[min] > this[j]) {
min = j;
}
}
if (min !== target) {
this.swap(min, target);
}
}
}
Here are the results of the same randomly generated array with 10000 elements:
BubbleSort => 148ms
InsertionSort => 94ms
SelectionSort => 91ms
MergeSort => 45ms
All the sorts are using the same swap method. So why is Selection Sort faster? My only guess is that Javascript is really fast at array traversal but slow at value mutation, since SelectionSort uses the least in value mutation, it's faster.
** For Reference **
Here is my Bubble Sort implementation
Array.prototype.bubbleSort = function () {
for (var i = this.length - 1; i > 1; i--) {
var swapped = false;
for (var j = 0; j < i; j++) {
if (this[j + 1] < this[j]) {
this.swap(j, j+1);
swapped = true;
}
}
if ( ! swapped ) {
return;
}
}
}
Here is the swap Implementation
Array.prototype.swap = function (index1, index2) {
var val1 = this[index1],
val2 = this[index2];
this[index1] = val2;
this[index2] = val1;
};
First let me point out two flaws:
The code for your selection sort is faulty. The inner loop needs to be
for (var j = target + 1; j < this.length; j++) {
otherwise the last element is never selected.
Your jsperf tests sort, as you say, the "same randomly generated array" every time. That means that the successive runs in each test loop will try to sort an already sorted array, which would favour algorithms like bubblesort that have a linear best-case performance.
Luckily, your test array is so freakishly large that jsperf runs only a single iteration of its test loop at once, calling the setup code that initialises the array before every run. This would haunt you for smaller arrays, though. You need to shuffle the array inside the "timed code" itself.
Why is Selection Sort faster? My only guess is that Javascript is really fast at array traversal but slow at value mutation.
Yes. Writes are always slower than reads, and have negative effects on cached values as well.
SelectionSort uses the least in value mutation
Yes, and that is quite significant. Both selection and bubble sort do have an O(n²) runtime, which means that both execute about 100000000 loop condition checks, index increments, and comparisons of two array elements.
However, while selection sort does only O(n) swaps, bubble sort does O(n²) of them. That means not only mutating the array, but also the overhead of a method call. And that much much more often than the selection sort does it. Here are some example logs:
> swaps in .selectionSort() of 10000 element arrays
9989
9986
9992
9990
9987
9995
9989
9990
9988
9991
> swaps in .bubbleSort() of 10000 element arrays
24994720
25246566
24759007
24912175
24937357
25078458
24918266
24789670
25209063
24894328
Ooops.

Debugging loop and function - javascript

My browser is crashing from this loop that doesn't appear to be unterminated.
function checkLetters(word){
for(i=0;i<5;i++){
for(j=i+1;j<5;j++){
if(word.charAt(i) == word.charAt(j)){
return false;
break;
}
}
}
return true;
}
var compLibrary = [];
for (var k=0;k<library.length;k++) {
if(checkLetters(library[k]) == true) {
compLibrary.push(library[k]);
}
}
I am trying to search the library for words with no repeating letters and pushing it into a new array
The whole library is five letter words.
It's not an infinite loop, but it does look like a pretty expensive operation. There's not any really elegant way to detect an infinite loop (or recursion) so most engines just resort to either
Not trying to detect it, and running forever until some lower-level controller (like, the kernel) kills it.
Automatically killing itself when it gets to a certain recursion depth, loop count, or run time.
Your algorithm loops 5 * 4 * library.length times, so depending on how long your library actually is, your code could certainly trigger #2. But there are faster ways to find duplicate letters:
function checkLetters(word) {
var i=word.length;
var seenChars={};
var c;
while (i-->0) {
c = word.CharAt(i); # The current character
if (c in seenChars) return false;
seenChars[c] = 1;
}
return true;
}
var compLibrary = [];
for (var k=0; k < library.length; k++) {
if (checkLetters(library[k]) == true) {
compLibrary.push(library[k]);
}
}
Shouldn't this loop
for(i=0;i<5;i++){
for(j=i+1;j<5;j++){
be something in these lines
for(var i=0; i< word.length ;i++){
for(j=i+1; j<word.length/2; j++){
Can't see what your issue is but here's the solution I suggest for your problem:
var words = ['hello', 'bye', 'foo', 'baz'];
function hasUniqLetters(word){
var uniq = word.split('').filter(function(letter, idx){
return word.indexOf(letter) == idx;
});
return uniq.length == word.length;
}
var result = words.filter(hasUniqLetters);
console.log(result); //=> ["bye","baz"]
function checkLetters(word){
for(i=0;i<5;i++){ //both i and j were not instantiated
for(j=i+1;j<5;j++){
if(word.charAt(i) == word.charAt(j)){ //It could be crashing if
return false; //word <= 5
break;//a break after a return
} //statement is redundant.
}
}
return true;
}
You must put var before declaring a variable.
word.charAt(i) can be written like word[i]
Try this:
function checkLetters(word){
for(var i=0,j=1,l=word.length-1; i<l; i++,j++){
if(word.charAt(i) == word.charAt(j)){
return false;
}
}
return true;
}
var compLibrary = [];
for(var i=0,l=library; i<l; i++){
if(checkLetters(library[i]) == true){
compLibrary.push(library[i]);
}
}
tldr; The code originally posted should not crash the browser.
The following explains why nested loops are not always bad for efficiency and shows a counter-example where the original code works successfully without crashing the browser when running over 100,000 simulated words.
The complexity of the posted code is low and it should run really fast. It executes here in a fraction of a second (under 20ms!), even at all "20 * 8000" - i.e. C * O(n). Note that the time complexity is linear because the nested loops in checkLetters have a constant time: due to this small fixed limit ("20 loops" max each call) it does not represent a performance problem here.
As such, I maintain that it is not an issue wrt it being an efficiency problem. I assert that the original code will not "crash" a modern browser environment. For longer or unbound words then using a (presumably) lower complexity probe attempt may pay off - but the inner loop runs in small constant time here. (Actually, due to distribution of letters within words and word lengths I would imagine that the constant rarely exceeds "90 loops" for a natural language like English.)
See http://jsfiddle.net/FqdX7/6/
library = []
for (w = 0; w < 100000; w++) {
library.push((w + "12345").slice(0,5))
}
function checkLetters(word){
for(i=0;i<5;i++){
for(j=i+1;j<5;j++){
if(word.charAt(i) == word.charAt(j)){
return false;
}
}
}
return true;
}
$('#time').text("running")
start = +(new Date)
var compLibrary = [];
for (var k=0;k<library.length;k++) {
if(checkLetters(library[k]) == true) {
compLibrary.push(library[k]);
}
}
time = +(new Date) - start
$('#time').text(time + "ms")
On my machine (in Safari) the code runs in ~30 milliseconds (~40ms if the "return false" is removed) for an input of 100,000 words!
In comparison, the answer with a probe (seenChars lookup) actually runs worse in Safari/Chrome. See http://jsfiddle.net/Hw2wr/5/, where for 100k words it takes about 270ms - or about 9x slower. However, this is highly browser dependent and the jsperf in the comments shows that in Firefox the probing approach is faster (by about 2x) but is slower again in IE (say 4-5x).
YMMV. I think the original code is acceptable for the given situation and the "crashing" problem lies elsewhere.

Javascript code help - Wrong return

So I'm working on this script. When I'm done with it, it should be used for making 2-and-2 groups. But anyway; The 'input' array in the start of the script, will get 22 different inputs from my HTML file. As a standard, I gave them the values 1-22. The thing is my two blocks '1st number' and '2nd number' doesn't work very well: they don't return the right numbers. Cause I want every elev[x] to be used once! Not 2 times, not 0 times, once! And the blocks returns like some of them twice and some of them isn't even used. So how can I fix this?
function Calculate(){
var elev = [];
var inputs = document.getElementsByName("txt");
for(i=0; i<inputs.length; i++) {
elev[i] = {
"Value": inputs[i].value,
"Used": false
};
}
function shuffle(elev){
var len = elev.length;
for(i=1; i<len; i++){
j = ~~(Math.random()*(i+1));
var temp = elev[i];
arr[i] = elev[j];
arr[j] = temp;
}
}
for(var v=0; v<1; v++) {
shuffle(elev);
document.write(elev + '<br/>\n');
}}
Yes, I'm still new at programming and I just wanna learn what I can learn.
Problem solved by doing the Fisher-Yates shuffle.
The idea of shuffling an array and iterating over it is correct, however, sorting by a coin-flipping comparator (a common mis-implementation; suggested by the other answer) is not the correct way to shuffle an array; it produces very skewed results and is not even guaranteed to finish.
Unless you're willing to fetch underscore.js (arr = _.shuffle(arr)), It is recommended to use the Fisher-Yates shuffle:
function shuffle(arr){
var len = arr.length;
for(var i=1; i<len; i++){
var j = ~~(Math.random()*(i+1)); // ~~ = cast to integer
var temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
...
shuffle(elev);
for(var i=0; i<elev.length; i++){
//do something with elev[i]
console.log(elev[i].Value);
}
also, note that object fields should be lowercase, not uppercase (value', not 'Value'). Also, theUsed` field should not be neccessary since you now iterate in order. Also, any chance you could use an array of Values? They seem to be the only field in your objects.
Also, Don't use document.write(). It doesn't work as expected when used after the page load. If you want to append something to the document, you hate jQuery and other frameworks and you don't want to go the long way of creating and composing DOM nodes, document.body.innerHTML += is still better than document.write() (but still consider appending DocumentFragments instead').
demo: http://jsfiddle.net/honnza/2GSDd/1/
You are only getting random numbers once, and then processing them if they haven't already been used.
I would suggest shuffling the array (elev.sort(function() {return Math.random()-0.5})) and then just iterating through the result without further random numbers.

Categories