Performance cost of the for-of loop - javascript

Recently I was experimenting with ES6 code using babel. I was quite surprised to see the verbose code of a compiled for of loop. Here is an example:
ES6
const a = [1, 2, 3, 4, 5];
for (const i of a)
{
console.log(i);
}
Compiled
"use strict";
var a = [1, 2, 3, 4, 5];
for (
var _iterator = a,
_isArray = Array.isArray(_iterator),
_i = 0,
_iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();
;
) {
var _ref;
if (_isArray) {
if (_i >= _iterator.length) break;
_ref = _iterator[_i++];
} else {
_i = _iterator.next();
if (_i.done) break;
_ref = _i.value;
}
var i = _ref;
console.log(i);
}
Whereas standard for loops, or a .forEach loop look almost exactly the same compiled as they do in ES6. I use the for of loop quite a lot in my code, it is a nice concise way to iterate over arrays. But until now I was unaware of all the added state and logic this brings with it. I would like to know whether using this compared to a normal for loop or .forEachhas a non-negligible performance cost?

I would like to know whether using this compared to a normal for loop or .forEach has a non-negligible performance cost?
That completely depends on the size of your array.
Unless you're iterating over millions of elements, you're not going to notice a difference.
Babel expands the loop this much for compatibility purposes. Older browsers don't have for...of, but the functionality can be reproduced like that.

Related

imperative vs declarative JavaScript

I am doing an exercise about imperative vs declarative. Unfortunately I don't understand the example or I don't get any results. "Uncaught ReferenceError: range is not defined"
'use strict'
// imperative
let numbers = range(10)
let evenNumbers = []
for (let i = 0; i < numbers.length; i += 1) {
if (numbers[i] % 2 === 0) {
evenNumbers.push(numbers[i])
}
}
console.log(evenNumbers) // => [0, 2, 4, 6, 8]
and:
// declarative
range(10).filter(v => v % 2 === 0) // => [0, 2, 4, 6, 8]
It seems you come from a Python or PHP background, but JavaScript does not have a global range()-function. See this post for an alternative.
Edit: A simple solution in this case would simply be to write:
let numbers = [0,1,2,3,4,5,6,7,8,9]
Sure, it's a bit verbose, but it's also very easy to read, and in a small case like this, it's not problematic.
If the purpose of the exercise was the difference between imperative and declarative programming, range(2) vs [0,1,2] should not matter.

Iterating through a function call

After years of writing loops in C++ the tedious way
for(int i=0; i<N; ++i) {
...
}
it becomes quite nice to use iterators
for(it i=v.begin(); i<v.end(); ++i) {
...
}
and ultimately moving to range iterators
for(auto i:v) {
...
}
In JavaScript also the for can be used, in a style nearly identical
(minus the type declaration and the pre/post increment operator) to
the first one above.
Still, in all of these the for is there. The D3.js
library demonstrates an alternative. One can iterate over an array by writing
d3.select("body")
.selectAll("p")
.data([4, 8, 15, 16, 23, 42])
.enter().append("p")
.text(function(d) { return "I’m number " + d + "!"; });
Here the enter mutates to a for loop. The documentation
explains nicely the client-side view of joins. What I am missing is a
standalone example of the (functional programming?) style of
converting a function call to an iteration.
No doubt this is not unique to D3.js. This is just where I encountered the idiom.
Can you suggest a few lines of standalone JavaScript code that
demonstrate iteration through a function call?
There are at least a couple of built-in functions that come to my mind.
map()
This one is very obvious.
[1, 2, 3]
.map(someNumber => someNumber * someNumber)
.map((powered, index) => index + "::" + powered);
// --> [ "1::1", "2::4", "3::9" ]
Chains well, right? Takes some input and produces the result consisting of elements calculated by applying a function element-wise.
Recommendation: try to use with pure functions whenever possible (produce the same results for same inputs, don't mutate the original collection if possible, nor produce any side effects).
forEach()
This function iterates through all elements of an array too, and applies a function, without returning anything back. Therefore, it can only end a chain of calls, but cannot be used for further chaining.
[1, 2, 3, 4]
.forEach(number => console.info(number));
Recommendation: forEach() is useful when we want to write some code that will result in a side effect per entry in the collection being iterated.
filter()
Filter function uses a predicate is used to sift the wheat from the chaff. The predicate is defining a criteria for the items you want to deal with on the next "stage".
[null, undefined, 0, 1, 2, 3, NaN, "", "You get the idea"]
.filter(Boolean)
.map(filteredElement => filteredElement + "!")
// --> [ "1!", "2!", "3!", "You get the idea!" ]
Recommendation: try to use with pure functions whenever possible. I.e. don't do anything else in filter other than things immediately related to filtration logic itself.
Object.keys() and Object.entries()
These two functions are helpful when we need to iterate over object's keys or key-value pairs, rather than an array's elements.
const targetObject = { a: 1, b: 2, c: 3 };
Object
.keys(targetObject)
.map(key => key + "=" + targetObject[key])
// --> [ "a=1", "b=2", "c=3" ]
same result can be achieved like this
Object
.entries({ a: 1, b: 2, c: 3 })
.map((key, value) => key + "=" + value)
// --> [ "a=1", "b=2", "c=3" ]
Recommendation: you may want to use Object.hasOwnProperty(...) when using working with Object.keys(...). See the documentation for details.
find()
The one is almost trivial. Let's us search for an item that matches a predicate. The search is "left-to-right", and it stops whenever the first "match" is found.
[1, 5, 10, 15]
.find(number >= 7)
// --> 10
findIndex() function can be used when we're looking for a position of an element that matches a predicate.
some() and every()
These functions check whether
a) there is at least one element matching a predicate; or
b) each and every element is matching a predicate.
const arrayOfNumbers = [2, 4, 6, 8, 10];
arrayOfNumbers.every(number => number % 2 === 0); // --> true
arrayOfNumbers.every(number => number % 2 === 1); // --> false
arrayOfNumbers.some(number => number > 1); // --> true
arrayOfNumbers.some(number => number <= 1); // --> false
reduce() and `reduceRight()`
The last one to mention in this quick review is the function that takes a list of things and aggregates it into a single result.
[-1, 0, 1, 2, 3]
.filter(value => value >= 0) // [0, 1, 2, 3]
.map(value => value + 1) // [1, 2, 3, 4]
.reduce((subTotal, currentValue) => subTotal + currentValue, 5);
// --> 15
Recommendation: try to use with pure functions whenever possible.
Universally applicable note on performance. In my benchmarks (don't have them on hand), a hand-written for loop was always faster than forEach, map, and other iterating functions. I do still prefer the functions unless the performance is being severely affected. There two main reasons for that: 1) easier to avoid off-by-one-errors; 2) the code is more readable, since each single function defines an independent step in the data processing flow, thus making code simpler and more maintainable.
I hope, this is an okay overview of some built-in chain-able JavaScript functions. More are described here. Take a look at concat(), sort(), fill(), join(), slice(), reverse() -- I frequently use them too.
If you need something like first() or last(), you will not find them in native functions. Either write your own ones, or use third-party libraries (e.g. lodash, rambda.js).
Here is an example implementation of Array.prototype.forEach:
function foreach(array, cb) {
for (var i = 0; i < array.length; ++i)
cb(array[i], i, array);
}
foreach([2,8,739,9,0], (n, i) =>
console.log("number: %s\nindex: %s\n", n, i));
surely I don't have to spoonfeed you do I?
function array_iterator(array) {
var i = 0;
function next() {
return array[i++];
}
function head() {
return array[i];
}
function tail() {
return array[array.length-1];
}
function more() {
return i < array.length;
}
function done() {
return !more();
}
function reset() {
i = 0;
}
return { next, head, tail, done, more, reset };
}
var nums = [3,34,4];
var iter = array_iterator(nums);
while (iter.more()) {
console.log(iter.next());
}

Side effects when async operating on arrays

I'm learning node.js atm, now I'm asking myself:
How "threadsafe" are normal Arrays?
Example:
var myArr = ["Alpha", "Beta", "Gamma", "Delta"];
ee.on('event', function(itemString) {
//Loop over an Array that could change its length while looping through
for(var i=0; i<myArr.length; i++) {
// delete the item out of the array
if(myArr[i] == itemString)
myArr.splice(i,1);
}
});
If multiple of the Events are fired on the ee-Object, is there a chance, that the for Loop will fail because the indexes are already spliceed away?
Or said different: Is a way to ensure that the loop won't skip or fail because any elements that may be deleted by another callback call of the same event?
THX :)
node.js is single threaded and it does not interrupts sync execution.
Still, you're modifying the array while iterating it by its length which may lead to skipping elements.
Also, your event is not prepared to be fired twice for the same array element.
I think we've covered the threading issue well, but you really should still address the loop. For an example of the "skipping" problem I'm talking about, try this:
var a = [1, 2, 3, 4, 5];
for (var i = 0; i < a.length; i++) {
console.log(a[i]);
if (a[i] === 2) {
a.splice(i, 1);
}
}
Output:
1
2
4
5
Notice how the number 3 is never even seen by this loop.
One common way to fix this kind of loop so you can safely delete elements of the array while iterating over it is to go backwards:
var a = [1, 2, 3, 4, 5];
for (var i = a.length - 1; i >= 0; i--) {
console.log(a[i]);
if (a[i] === 2) {
a.splice(i, 1);
}
}
Output:
5
4
3
2
1
Notice that we see all the elements of the array this way.

JavaScript: How to add x to an array x times?

I imagine this is similar to array padding, but I wonder if it can be simplified at all.
var arr = [1,2,3],
x = 5;
for (var i=0; i<x; i++) {
arr.push(x);
}
console.log(arr);
//=> [1, 2, 3, 5, 5, 5, 5, 5]
Is there any way to do this without using the for loop?
Update
Even though there's mover clever solutions, the for-loop seems to be the most performant
Benchmarks on jsperf
Unless you get penalised for each line of code you write, that code is fine: succinct and very understandable.
If you do get penalised, just use:
for (var i = 0; i < x; i++) arr.push(x);
on a single line :-)
Beyond that, your best bet would be a function along the lines of:
arr = appendXCopiesOfX (arr, x);
but I don't think you're really gaining anything there since, as mentioned, there should be very little problem understanding such a small loop.
All in all, it's probably a waste of time and effort trying to improve what you currently have.
Without for loop:
var arr = [1,2,3], x = 5;
arr = arr.concat(Array.apply(null, Array(x)).map(function() { return x; }));
// or even
arr = arr.concat(Array.apply(null, Array(x)).map(x.valueOf, x));
For those using ES6 and have come across this issue. Try using Array.fill() https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/fill
I used this method in testing to generate mock data for x rows without needing to loop.
Edit: Sorry, I was off on this one. This code will generate an array x long, but they would all have the same value. A better code snippet is at the end of this post.
newOrdersX: function(x) {
return Array(x).fill({
name: faker.random.name()
});
}`
This one will be an array x levels long, but each value will be different. Note that .fill() still needs to be called or it won't work.
newOrdersX: function(x) {
return Array(x).fill().map(() => ({
name: faker.random.name()
}));
}`
Another ES6 option is to use Array.from() to create an array with the length of the original array + x:
const pad = (arr, x) =>
Array.from({ length: arr.length + x }, (_, i) => arr[i] ?? x)
const arr = [1,2,3], x = 5;
console.log(pad(arr, x));

Tersest way to create an array of integers from 1..20 in JavaScript

What would be the tersest way to create this array:
var x = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
11, 12, 13, 14, 15, 16, 17, 18, 19, 20];
For example, a for loop:
var x = [];
for (var i=1;i<=20;i++) {
x.push(i);
}
Or a while loop:
var x = [], i = 1, endInt = 20;
while (i <= endInt) {
x.push(i);
i++;
}
Would there be other examples that would be terser -- in other words -- less code? I'm thinking of things like in Ruby where the equivalent code I believe would be as simple as 1..20. I'm not aware of syntax like that in JavaScript but I'm wondering if there are shorter ways to do that same thing.
UPDATE: I wasn't thinking of removing semicolons or var for answers in the question, but I have to admit the question implies that. I am more curious about algorithms than shaving bytes. Sorry if I was unclear! Also, making it into a function is simple enough, just slap function range(start, end) { /* guts here */ } around it and you're there. The question is are there novel approaches to the "guts."
Favorite method
Update Sep13,2015:
Just came up with this new method which works with browsers which support the ES6 standard:
> Array(5).fill().map((x,i)=>i)
[0, 1, 2, 3, 4]
Note the above does a tiny bit of extra work (fills with undefined) but is relatively minor vis-a-vis the speedup you can achieve by using a for loop, and if you forget the .fill you may be confused why your array is mysteriously [empty x 5]. You can encapsulate the above as a custom function, or alternatively use a somewhat more intended method:
> Array.from(Array(5),(x,i)=>i)
[0, 1, 2, 3, 4]
You can of course directly go from that into whatever you want to do, like python's list comprehensions e.g. [i**2 for i in range(5)]:
> Array.from(Array(5), (_,i)=> i**2)
[0, 1, 4, 9, 16]
... or if you want to get more complicated...:
> Array.from(Array(5), (_,i)=> {
const R = /*some computation*/;
return /*etc*/;
});
[edit May,2021]: theoretically tersest way of defining such a function nowadays is f=i=>i?[...f(i-1),i]:[], where you replace f with range1 or whatever the name is, but which would be very slow (quadratic complexity) due to intermediate structures so should never be used. f=i=>i?f(i-1)&&x.push(i)&&x:x=[] is linear complexity but relies on abuse of notation and is unreadable and pollutes global variables as well. But, since defining arrow functions (which don't bind but rather inherit this) is pretty terse nowadays, you could just wrap the above solution:
const range1 = n=> Array.from(Array(n), (_,i)=> i+i);
// range1(5)==[1, 2, 3, 4, 5]
Circumstantially, the tersest way to do a range(N), if you already have a list lying around of exactly that length N, is just to map it: e.g. rather than do Array.from(Array(myArr.length), (_,i)=> i**2), you would just do myArr.map((_,i)=> i**2). (This has no side-effect unless you want it to.)
everything below is historical:
After thinking about it a bit, this is the shortest implementation of the standard range(N) function in JavaScript I could come up with:
function range1(i){return i?range1(i-1).concat(i):[]}
Note: Do not use this in production; it's O(N^2)
Contrast with current top-voted answer:
function range1(i){var x=[];var i=1;while(x.push(i++)<i){};return x}
Example:
> range1(5)
[1, 2, 3, 4, 5]
This is like the poster child for recursion, though I was expecting it to be longer until I thought of ternary-if-statement, which brings it down to 42 necessary characters.
Note that the "standard" range function returning [start,end) can be written by doing .concat(i-1).
Update: Ooh, I discovered an incredibly short version with ugly imperative syntax by abusing for loops, reverse-ordering, the fact that assignments return a value: for(y=[],i=20;y[--i]=i;){} consisting of only 25 characters (though you will want var y which you can insert into a for loop, and +1 if you don't want 0...19). While it is not shorter if you need to define a function, it is shorter than i?r(i-1).concat(i):[] if you do not need to make a function.
Added some performance profiling testcases: it seems that everything besides a standard in-order for-loop is 10x slower, at least on V8. https://jsperf.com/array-range-in-javascript
(Of course, none of this matters if you're programming in a functional style anyway and would hit every element with a function call anyway.)
It can be done with features from the ES6, currently only supported by Firefox thou. I found a compatibility table here: http://kangax.github.io/compat-table/es6/
Array.from(new Array(20), (x,i) => i+1)
If you want to have some other range then I guess you could do
Array.from(new Array(5), (x,i) => i+5)
Which would then be [5,6,7,8,9]
You can do this with a while loop where the push happens inside the condition.Array.push returns the length of the array, which happens to be the same as the value in this case. So, you can do the following:
x = []; //normally would use var here
i = 1; //normally would use var here
while(x.push(i++)<20){}
//at this point, x = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
Condensed version (31 characters)
x=[];i=1;while(x.push(i++)<20);
jsFiddle example
while-- is the way to go
var a=[],b=10;while(b--)a[b]=b+1
returns [1,2,3,4,5,6,7,8,9,10]
explained with start & length
var array=[],length=20,start=5;while(length--)array[length]=length+start
returns [5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24]
want range?
explained with start & end
var array=[],end=30,start=25,a=end-start+1;while(a--)array[a]=end--
returns [25, 26, 27, 28, 29, 30]
for --
for(var a=[],b=20;b>0;b--,a[b]=b+1)
for++
for(var a=[],b=0;b<20;b++,a[b]=b+1)
WHY is this theway to go?
while -- is prolly the fastest loop;
direct setting is faster than push & concat;
[] is also faster than new Array(10);
it's not much longer code than all the others
byte saving techniques:
use the arguments as a placholder forthe in function variables
don't use new Array(),push(),concat() if not needed
place "(){};," only when needed.
use a,b,c,d... in short functions.
so if u want a function for this
with start,end (range)
function range(a,b,c,d){d=[];c=b-a+1;while(c--)d[c]=b--;return d}
so now range(3,7) returns [3,4,5,6,7]
u save bytes in many ways here and this function is also very fast as it does not use concat, push, new Array and it's made with a while --
If you're OK with 0-20, here are my latest favs from recent code golfing:
[...'0'.repeat(21)].map((_,i)=>i)
Array.from({length:21},(_,i)=>i)
Array(21).fill().map((_,i)=>i)
[...Array(21)].map((_,i)=>i)
Array(21).map((_,i)=>i)
[...Array(21).keys()]
Using ES6
numArr = Array(5).fill(0).reduce(arr=>{ arr.push(arr.length); return arr },[])
if you accept to have a counter starting from 0 instead of 1...
const zeroNineteen = [...Array(20).keys()];
// outputs [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19]
if you really need it to start with 1:
const terserst = [...Array(21).keys()].slice(1);
it's 30 characters and yet readable, don't you think?
I can't think of a way with less characters than ~46:
var a=[];while(a.length<20)a.push(a.length+1);
Granted, you could make a function out of that.
Reading your comments about a function, you could do something like
var range = function (start, end) {
var arr = [];
while (start <= end) {
arr.push(start++)
}
return arr;
};
Then range(1, 20) would return the array as expected.
I suppose this is the shortest way:
var i=0, arr = [];
while (i++<20){
arr.push(i);
}
or associating on the 'perverse' code in EndangeredMassa's answer:
var i,arr; while (i=i||1, (arr=arr||[]).push(i++)<20){}
You could always create a function...
function createNumArray(a, b) {
var arr = [],
i = a;
while((arr[arr.length] = i) < b) {i++}
return arr;
}
Which allows you to write succinct code later on such as...
var arr = createNumArray(1, 20);
If you are looking to shave characters off anyway possible without regard for readability, this is the best I can do:
var x=[],i=0
while(i<20)
x[i]=i+++1
Not a lot better than yours though.
Edit:
Actually this works better and shaves off a couple characters:
var x=[],i=0
while(i<20)
x[i]=++i
Edit 2:
And here's my entry for a general "range" function in the least number of characters:
function range(s,e){var x=[];while(s<e+1)x.push(s++);return x}
Again, don't write code this way. :)
Array.from({length: n}).map((_, i) => i);
In my knowledge, the option of using for loop, as you mentioned, is the most tersest.
That is,
var x = [];
for (var i=1;i<=20;i++) {
x.push(i);
}
var i = 0;
var x = [];
while (i++ < 20) x.push(i);
JSFiddle
I'd extend Array's prototype to make it simple to access:
Array.prototype.range = function(start, end) {
if (!this.length) {
while (end >= start) {
this.push(start++);
}
} else {
throw "You can only call 'range' on an empty array";
}
return this;
};
var array = [].range(1, 20);
While the above is the nearest I can think of with respect to the syntactic sugar you're looking for, you may want to try out CoffeeScript.
It supports the notation you're after.
CoffeeScript:
test = [1..20]
alert test
Renders to JavaScript:
var test;
test = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20];
alert(test);
You can try out live examples on their site, and see the conversion it does as you type.
Just click the TRY COFFEESCRIPT link at the top, and you'll get a console where you can test some code.
There's always the IterableInt:
for (const z of new IterableInt(15)) {
// 15,14,13....3,2,1
}
implemented like so:
class IterableInt {
constructor(val) {
if (val < 0) {
throw new Error('cannot iterate over a negative number.')
}
this.val = val;
}
static create(val){
return new IterableInt(val);
}
[Symbol.iterator]() {
const self = this;
return {
next() {
const value = self.val--;
const done = value <= 0;
return {value, done}
}
}
}
}
to do things like:
for (const z of new IterableInt(3,15)) {
// 3,4,5...14,15
}
go to this gist:
https://gist.github.com/ORESoftware/1aca4ae704b355c45702d11c0e245776

Categories

Resources