What is the time complexity of a call to array.length in JavaScript? I think it would constant since it seems that property is set automatically on all arrays and you're just looking it up?
I think it would be constant since it seems that property is set automatically on all arrays and you're just looking it up?
Right. It's a property which is stored (not calculated) and automatically updated as necessary. The specification is explicit about that here and here amongst other places.
In theory, a JavaScript engine would be free to calculate length on access as though it were an accessor property as long as you couldn't tell (which would mean it couldn't literally be an accessor property, because you can detect that in code), but given that length is used repeatedly a lot (for (let n = 0; n < array.length; ++n) springs to mind), I think we can assume that all JavaScript engines in widespread use do what the spec says or at least something that's constant time access.
Just FWIW: Remember that JavaScript's standard arrays are, in theory, just objects with special behavior. And in theory, JavaScript objects are property bags. So looking up a property in a property bag could, in theory, depend on how many other properties are there, if the object is implemented as some kind of name->value hashmap (and they used to be, back in the bad old days). Modern engines optimize objects (Chrome's V8 famously creates dynamic classes on the fly and compiles them), but operations on those objects can still change property lookup performance. Adding a property can cause V8 to create a subclass, for instance. Deleting a property (actually using delete) can make V8 throw up its hands and fall back into "dictionary mode," which substantially degrades property access on the object.
In other words: It may vary, engine to engine, even object to object. But if you use arrays purely as arrays (not storing other non-array properties on them), odds are you'll get constant-time lookup.
It doesn't seem like a bottleneck but if you want to be sure use var len = arr.length and check that. It doesn't hurt and seems to be a tad faster on my machine albeit not a significant difference.
var arr = [];
for (var i = 0; i < 1000000; i++) {
arr[i] = Math.random();
}
var start = new Date();
for (var i = 0; i < arr.length; i++) {
arr[i] = Math.random();
}
var time1 = new Date() - start;
var start = new Date();
for (var i = 0, len = arr.length; i < len; i++) {
arr[i] = Math.random();
}
var time2 = new Date() - start;
document.getElementById("output").innerHTML = ".length: " + time1 + "<br/>\nvar len: " + time2;
<div id="output"></div>
Related
Can this be optimized
var groups = [];
for (var a in aList) {
for (var bNumber in bList) {
groups.push({ a: a, b: b });
}
}
The code is actually fine, but I just realized that it looks like a cross product of two lists, so instead of looping aList.length*bList.length times, I wondered if there was some smart function to do this.
Julian pointed out a mistake in my original answer where I was storing elements as opposed to indices.
The existing code is erroneous; there is no variable b.
I will assume bNumber was meant to be b (or the other way around; it doesn't matter).
Since you know that arrays are indexed 0...n (I am assuming you declared the arrays in a way such that all indices in that range exist), you certainly shouldn't use a for...in loop (as mentioned in this comment);
rather, you should explicitly code in the bounds in a standard for loop (or some nearly identical variant):
var groups = [];
for (var a = 0; a < aList.length; a++){
for (var b = 0; b < bList.length; b++){
groups.push({a, b});
}
}
You shouldn't generally be using var either;
you generally want to restrict the scope of your variables to directly within the block in which you are working, and you want to be able to make some variables unassignable.
const groups = [];
for (let a = 0; a < aList.length; a++){
for (let b = 0; b < bList.length; b++){
groups.push({a, b});
}
}
With those changes, this has runtime of around 11% of the code in the answer, with the error fixed.
Original answer, assuming storing elements
First, since you know that you are dealing with lists, you should not use a for...in loop (as mentioned in this comment).
Next, this operation is generally called the Cartesian product or direct product, not the cross product.
The existing code is erroneous; there is no variable b.
I will assume bNumber was meant to be b (or the other way around; it doesn't matter).
The code can then be rewritten as
var groups = aList.flatMap(a => bList.map(b => ({a, b})))
(Although you shouldn't generally be using var either!
You generally want to restrict the scope of your variables to directly within the block in which you are working.)
You will almost always find that the code in the question (with the error fixed) performs better, though, because Array maps have to handle some additional logic costs, like orchestrating the callbacks and list concatenation.
Switching your for...in loops to for...of loops actually halves the runtime in my tests.
According to the 5.2.1 section of this article: Optimization killers
Doing this turns optimizations off in V8:
function hashTableIteration() {
var hashTable = {"-": 3};
for(var key in hashTable);
}
and the author says:
An object will go into hash table mode for example when you add too many properties dynamically (outside constructor), delete properties, use properties that cannot be valid identifiers and so on. In other words, when you use an object as if it was a hash
table, it will be turned into a hash table. Passing such an object to for-in is a no no. You can tell if an object is in hash table mode by calling console.log(%HasFastProperties(obj)) when the flag --allow-natives-syntax is enabled in Node.JS.
My question is then what is the correct way of iterating through the keys of a hashtable-like object in javascript, so that optimization do not get turned off?
Looks like the answer lies at the bottom of the very same article.
Workaround: Always use Object.keys and iterate over the array with
for loop. If you truly need all properties from entire prototype
chain, make an isolated helper function:
function inheritedKeys(obj) {
var ret = [];
for(var key in obj) {
ret.push(key);
}
return ret;
}
If you pass a object to for-in that is not a simple enumerable it
will punish the entire containing function.
From what I understood, the isolated function would help in allowing the rest of the process to be optimized. Only the inheritedkeys function wouldn't be optimized in the example below.
function someFunction(someObj) {
var keys = inheritedKeys(someObj),
i = 0,
len = keys.length;
for (; i < len; i++) {
//some computation
}
}
I believe Object.keys is regularly a better alternative in performance. However, I cannot say whether the optimizer converts it to a hashtable if used.
Update: Adding code for others stumbling upon this question.
var keys = Object.keys(myObj);
for (var i = 0; i < keys.length; i++) {
var value = myObj[keys[i]];
}
When iterating over a string or array (or anything else with a length property), I've always used a loop like this:
var foo = [...];
var i;
for(i=0; i<foo.length; i++) {
// do something
}
However, I just encountered someone who did this:
var foo = [...];
var fooLen = foo.length;
var i;
for(i=0; i<fooLen; i++) {
// do something
}
He said he thought the ".length" was recalculating the length, thus the loop would recalculate the length of the string/array over and over, so by saving its length to a variable it would be more optimized.
I always assumed length was just a value property because of the way it's used (it's not "asdf".length(), it's "asdf".length) but is this not the case?
There are some situations where putting the length into a local variable is faster than accessing the .length property and it varies by browser. There have been performance discussions about this here on SO and numerous jsperf tests. In a modern browser, the differences were not as much as I thought they would be, but they do exist in some cases (I can't seem to find those previous threads).
There are also different types of objects that may have different performance characteristics. For example, a javascript array may have different performance characteristics than the array-like object returned from some DOM functions like getElementsByClassName().
And, there are some situations where you may be adding items to the end of the array and don't want to be iterating through the items you add so you get the length before you start.
From MDC
for (var i = 0; i < a.length; i++) {
// Do something with a[i]
}
This is slightly inefficient as you are looking up the length property
once every loop. An improvement is this:
for (var i = 0, len = a.length; i < len; i++) {
// Do something with a[i]
}
Maybe not much of a difference with "regular" arrays, but for something like "node.children.length" I would err on the safe side and call it only once. CoffeeScript does that for you automatically.
Note that there is an actual difference in behaviour if the length can change during the loop.
It depends on if you are changing the foo's length.
var foo = [1,2,3];
while(foo.length){
foo.shift();
}
Obviously the code is keeping track of the foo's length, not simply remembering a value.
You can assign the length as a number in the loop.
for(i=0, L=foo.length;i<L; i++) {
// do something to foo[i]
}
What's the big O for JavaScript's array access when used as a hash?
For example,
var x= [];
for(var i=0; i<100000; i++){
x[i.toString()+'a'] = 123; // using string to illustrate x[alpha]
}
alert(x['9999a']); // linear search?
One can hope JS engines will not use a linear search internally O(n), but is this for sure?
Accessing object properties and array elements in JavaScript is syntacticly assumed to be done in constant time: O(1). Performance characteristics are not guaranteed in the ECMAScript specification, but all the modern JavaScript engines retrieve object properties in constant time.
Here's a simple example showing how access times grow when the container is x1000 times bigger:
var largeObject = {};
var smallObject = {};
var x, i;
for (i = 0; i < 1000000; i++) {
largeObject['a' + i] = i;
}
for (i = 0; i < 1000; i++) {
smallObject['b' + i] = i;
}
console.time('10k Accesses from largeObject');
for (i = 0; i < 10000; i++) x = largeObject['a' + (i % 1000000)];
console.timeEnd('10k Accesses from largeObject');
console.time('10k Accesses from smallObject');
for (i = 0; i < 10000; i++) x = largeObject['a' + (i % 1000)];
console.timeEnd('10k Accesses from smallObject');
Results in Firebug, Firefox 3.6.10 (Mac OS X 10.6.4 - 2.93Ghz Intel Core 2 Duo):
10k Accesses from largeObject: 22ms
10k Accesses from smallObject: 19ms
Results in Chrome Dev Tools 6.0.472:
10k Accesses from largeObject: 15ms
10k Accesses from smallObject: 15ms
Internet Explorer 8.0.7600 with Firebug Lite on Windows 7
10k Accesses from largeObject: 250ms
10k Accesses from smallObject: 219ms
First and foremost Arrays are in fact hashes. Always. That's why x[5] === x["5"]:
var x = [];
x[5] = 10;
alert( x[5] === x["5"] ); // true
Objects are hashes and Arrays are just special objects. If you want to use general hashes go for Objects. "Associative arrays" in Javascript are Objects. Arrays are for numerically indexed data. Arrays have a length property and Array-like methods like push, pop, sort, etc. which makes no sense to be used on hashes.
As for the big O for searching in Objects: it's implementation dependent.
Probably the 2 best things you can do to:
Check the source code of some browser implementations
Do some benchmark for big n and make your conclusion
The related part of the language specification:
4.3.3 object
An object is a collection of properties
and has a single prototype object.
8.6.2 Object Internal Properties and Methods
Array objects have a slightly
different implementation of the
[[DefineOwnProperty]] internal method.
Array objects give special treatment
to a certain class of property names.
phew! That was a long title.
I'm reading WROX' book on Professional JavaScript for web developers and I came across this sample code, and I was just wondering if that was best practice:
function convertToArray(nodes) {
array = new Array();
for (var i=0, len=nodes.length; i < len; i++) {
array.push(nodes[i]);
}
return array;
}
The thing that's got me scratching my head is the "len=nodes.length". Am I wrong in thinking that the first sentence in a for-loop is only run once? Is there a reason you'd want to set a variable (len) to the length of the nodeList before running through it? Would you do that to a normal array as well?
Thanks
That is for performance reasons. A local variable is faster for several reasons:
The the length will need to be accessed all the time in the loop, once per every iteration;
A local variable lookup is faster than member lookup;
If nodes is an array, then .length is a magic property that may take a bit longer to retrieve than a member variable.
If nodes is an ActiveX object, then .length might result in a method call into the object, so that's the most expensive operation of all.
While we're discussing micro-optimizations, the following should be even faster:
function convertToArray(nodes) {
var i = nodes.length,
array = new Array(i); // potentially faster than `array = []`
// -- see comments
while(i--)
array[i] = nodes[i];
return array;
}
It needs one less local variable, uses a while and not a for loop and uses array assignment instead of the function call push().
Also, because we're counting down we pre-allocate the array's slots, the array's length doesn't have to be changed each iteration step, but only on the first one.