The explanation for a dense array that I read from a few pages seem to be in contradiction to one another. I'd like some help understanding what it is.
While some links (search result 1, search result 2) suggest that it simply is an array where:
the elements of the array are known to be specific values; and
are assigned to the array at the time of its initialization.
The allusion there is that JavaScript arrays are dense.
It all makes sense up until here.
But this statement taken from the JavaScript Guide on the Mozilla Developer Network (MDN) says:
Since an array's length can change at any time, and data can be stored at non-contiguous locations in the array, JavaScript arrays are
not guaranteed to be dense; this depends on how the programmer chooses
to use them. In general, these are convenient characteristics; but if
these features are not desirable for your particular use, you might
consider using typed arrays.
And this has confused me now. My question is:
What does the statement on the MDN page mean when it says JavaScript arrays are not guaranteed to be dense? If it means that the following is not a dense array because one or more of its elements are undefined at the time of initialization, then why do the links I listed above seem to indicate that JavaScript arrays are indeed dense?
var array = new Array(1, , 3, ); // [1, undefined, 3, undefined]
"Dense" is in opposition to "sparse", and generally is used when talking about storage. For example, this array is dense:
a = [undefined, undefined, 2]
It can be stored in memory exactly like that: a sequence of three locations, the first two being undefined, the third being 2.
This array is sparse:
a = []
a[100000000] = 100000000
It is not stored in memory as a sequence of 100000001 locations, as it would be horribly inefficient. It is definitely not 100000000 places of undefined followed by 100000000. Rather, it just says 100000000th one is 100000000, and there is no space allocated to the first 100000000 elements.
(Actually, try to do this with 2 instead of 100000000, and you'll notice a curious thing: Chrome will display the dense array as [undefined, undefined, 2], but the sparse one as [undefined × 2, 2].)
Those articles say you can create an array being dense. This means that, at the time of creation, such arrays are dense, since in arrays like:
var a = new Array("foo", "bar", "baz");
var b = [2, 3, 5];
every element is set: from 0 to length-1, there is no undefined value. Or better said: every position from 0 to length-1 was assigned a value (even if the value is, actually, undefined).
However, you can make those arrays not dense anymore, by doing something like this:
a[20] = "bat";
That array, which was dense, is not dense anymore since the elements 0 1 2 and 20 (unlike elements in 3 to 19) are set to a value (this array has 4 elements, not 21).
Related
I was curious to see the speed differences between arrays and objects, so I set up a test for filling, accessing, and deleting 100,000 items from an array and object. Accessing and filling the array were about equal with a ~3ms difference. Deleting from the array, however, resulted in 604ms difference(10ms vs 614ms). Why is this? I thought objects and arrays were pretty much the same.
Demo: https://codecanister.com/Project/b9f8de7c/1/result/
When you do that .splice(), all the subsequent array entries have to be reassigned. That is, every property name after the one spliced out must be changed. There's no good way to do that except a straight linear traversal of the properties; a data structure that made that operation fast would make other, more common operations slower.
So consider the array [1, 2, 3, 4]. The value of property "0" is 1. If you splice that entry out, then the runtime has to set property "0" to 2, property "1" to 3, and property "2" to 4.
You just perform different actions. "delete" will just set the array-position to undefined. While splice will totally remove it by performing a loop with arr[i] = arr[i+1] for all 10,000 items of your array. You do this for all 10.000 items. See also this question
I have an array:
mydata =[];
i am storing values in the array by using a numeric key that can be somewhat big
mydata[13525] = 1;
However if i only store one item as the above, when i print the array in the console it prints 13524 commas before the object, nevertheless the debugger tells me it has length 13526 and there is only one element in the array.
I am confused as i understood that JS arrays do not required every position to be filled and certainly this might consume a lot of memory on bigger numbers. Can you explain please?
The .length property of an array represents the highest index plus one in the array that has been assigned a value.
When you do not assign intermediate values (and thus have a sparse array), the intervening values that you have not assigned are undefined which is essentially the "nothing here" value in Javascript. The console chooses to display arrays by trying to show you every value between 0 and the end of the array which is simply not a very good way to display a sparse array that is mostly empty. That's more an artifact of a design choice in the console than anything else. One could design a different way to display contents of an array that would handle sparse arrays differently.
Arrays are most efficient if you use consecutive indexes started from 0. That's what they are mostly designed for and what many implementations are optimized for since a runtime can do some optimized things if it knows there is a sequential set of values.
If you know you're going to mostly not be using sequences of numeric indexes starting from 0 and as such the .length property is of little use to you, then perhaps a plain object with arbitrary properties is a better choice.
var mydata = {};
mydata[13525] = 1;
console.log(mydata[13525]); // 1
console.log(mydata.length); // undefined - no .length property on an object
console.log(myData); // {1: 13525}
If you only want to print any non-null value in the array, instead of printing the whole array I'd use a for loop that only prints non-null values. It'd be something like this...
for (i = 0; i < mydata.length; i++) {
if(mydata[i]!= null){
console.log(mydata[i]);
}
}
javascript array is bit different from others,
var foo = [1, 2, 3, 4, 5, 6];
foo.length = 3;
foo; // [1, 2, 3]
foo.length = 6;
foo.push(4);
foo; // [1, 2, 3, undefined, undefined, undefined, 4]
While the getter of the length property simply returns the number of elements that are contained in the array, in setter, a smaller value truncates the array, larger value creates a sparse array. Guess what the setter mydata[13525] = 1; would do.
src: Javascript Garden
Edit:
to print/use only the values present, you can do
mydata.forEach(function(v){console.log(v);});
I read in a particular book that an array in JavaScript can hold 4,294,967,295 items and would throw exception if the number reaches beyond that.
I tried out the functionality using the following code:
var a = ["a","b","c"];
a[4294967300] = "d";
console.log(a[4294967300]);
It shows the output "d" and no exception or error. Am I missing something here? Can someone put some light on the topic and share some knowledge regarding max array items in JavaScript and various scenarios related to it?
An array doesn't have to hold all the items from 0 to N to contain one with index N.
That's because arrays in JavaScript engines can switch to a dictionnary mode when the holes are too big, those arrays are called sparse arrays (vs dense arrays).
It's important to know this distinction because the implementation is leaking on one point : performance. You should read this on this topic : http://www.html5rocks.com/en/tutorials/speed/v8/
But regarding indexes starting at 2³², sebcap26 is right, there's a distinction due to the fact the index is handled as a string. This distinction is important and can be verified by logging a.length : you'll see the length isn't impacted by such an element. There's no exception or error per se but it makes it impossible to use normal array operations like iterating up to the length or using array functions like map or filter (the elements with index greater than the numeric index limit are ignored by those functions).
If I understand well the ECMAScript specifications, an index which is not in [0 .. 2^32-1] is converted into a String and used as an Object key, not as an Array index.
A property name P (in the form of a String value) is an array index if and only if ToString(ToUint32(P)) is equal to P and ToUint32(P) is not equal to 2^32−1.
Try running this code: fiddle : http://jsfiddle.net/vXtfE/
var a = ["a","b","c"];
a[4294967300] = "d";
console.log(a.length);
console.log(a);
console.log(a[4294967300]);
You will see this output:
3
["a", "b", "c", 4294967300: "d"]
d
The initial items get stored as array elements, but for large index, the storage changes to hash based sparse array. Hence, it is a mix of both in your case.
Good explanation of this :
Why is array.push sometimes faster than array[n] = value?
I have a javascript array of objects that are each created with 'new'. In the event of an error I would like to clear the whole array so that it would be GC'ed by the JS engine. To this end is it sufficient to just set the array variable to 'null' or do I need to splice all the elements from the array and set them to null before setting the array variable to 'null'?
The reason I am asking is that in Firefox I displayed (console.log) the array before assigning it to null and the displayed object (which is usually updated in the display I assume even later) still shows the elements of the array when I inspect it later, hence my doubt if the elements are actually being free'd or not.
To clear an array you can just set the length to zero:
var arr = [1,2,3,4,5];
console.log(arr);
arr.length=0;
console.log(arr);
Result:
[1, 2, 3, 4, 5]
[]
Edit:
Just found this on the topic: http://davidwalsh.name/empty-array
Looking at the comments it seems that just setting the variable to a new array is the simplest method:
arr = [];
According to the test results the memory is GC'd quicker than setting the length to 0. I would imagine this is to do with the allocations triggering the GC.
There is an interesting performance test on various methods here: http://jsperf.com/array-destroy
Namely, how does the following code:
var sup = new Array(5);
sup[0] = 'z3ero';
sup[1] = 'o3ne';
sup[4] = 'f3our';
document.write(sup.length + "<br />");
output '5' for the length, when all you've done is set various elements?
My 'problem' with this code is that I don't understand how length changes without calling a getLength() or a setLength() method. When I do any of the following:
a.length
a['length']
a.length = 4
a['length'] = 5
on a non-array object, it behaves like a dict / associative array. When I do this on the array object, it has special meaning. What mechanism in JavaScript allows this to happen? Does JavaScript have some type of property system which translates
a.length
a['length']
into "get" methods and
a.length = 4
a['length'] = 5
into "set" methods?
Everything in JavaScript is an object. In the case of an Array, the length property returns the size of the internal storage area for indexed items of the array. Some of the confusion may come into play in that the [] operator works for both numeric and string arguments. For an array, if you use it with a numeric index, it returns/sets the expected indexed item. If you use it with a string, it returns/sets the named property on the array object - unless the string corresponds to a numeric value, then it returns the indexed item. This is because in JavaScript array indexes are coerced to strings by an implicit toString() call. Frankly, this is just one more of those things that makes you scratch your head and say "JavaScript, this, this is why they laugh at you."
The actual underlying representation may differ between browsers (or it may not). I wouldn't rely on anything other than the interface that is supplied when working with it.
You can find out more about JavaScript arrays at MDN.
Characteristics of a JavaScript array
Dynamic - Arrays in JavaScript can grow dynamically .push
Can be sparse - for example, array[50000] = 2;
Can be dense - for example, array = [1, 2, 3, 4, 5]
In JavaScript, it is hard for the runtime to know whether the array is going to be dense or sparse. So all it can do is take a guess. All implementations use a heuristic to determine if the array is dense or sparse.
For example, code in point 2 above, can indicate to the JavaScript runtime that this is likely a sparse array implementation. If the array is initialised with an initial count, this could indicate that this is likely a dense array.
When the runtime detects that the array is sparse, it is implemented in a similar way to an object. So instead of maintaining a contiguous array, a key/value map is built.
For more references, see How are JavaScript arrays implemented internally?
This really depends on what you intend to do with it.
[].length is "magical".
It doesn't actually return the number of items in the array. It returns the largest instated index in the array.
var testArr = []; testArr[5000] = "something"; testArr.length; // 5001
But the method behind the setter is hidden in the engine itself.
Some engines in some browsers will give you access to their implementations of those magic-methods.
Others will keep everything completely locked down.
So don't rely on defineGetter and defineSetter methods, or even, really, __proto__ methods, unless you know which browsers you know you're targeting, and which you aren't.
This will change in the future, where opt-in applications written in ECMAScript Next/6 will have access to more.
ECMAScript 5-compliant browsers are already starting to offer get and set magic methods in objects and there's more to come... ...but it's probably a while away before you can dump support for oldIE and a tonne of smartphones, et cetera...
It is important to know that when you do sup['look'] = 4; you are not using an associative array, but rather modify properties on the object sup.
It is equivalent to sup.look = 4; since you can dynamically add properties on JavaScript objects at any time. sup['length'] would for an instance output 5 in your first example.
To add to tvanfosson's answer: In ECMA-262 (the 3.0 specification, I believe), arrays are simply defined as having this behavior for setting properties (See 15.4.5.1). There's no general mechanism underlying it (at least as of now) - this is just how it's defined, and how JavaScript interpreters must behave.
As other people have mentioned, a property in JavaScript can basically act as both as getter and a setter of your array (or string or other inputs).
As a matter of fact, you might try this yourself:
const test = [1, 2, 3, 4, 5]
test.length = 3
console.log(test) // [1, 2, 3]
test.length = 5
console.log(test) // Guess what happens here!
As far as I know, arrays in JavaScript do not work exactly like associative arrays and you have elements which are put in memory as contiguously as possible (given that you can have arrays of mixed objects), depending on the JavaScript engine you are considering.
As a side note, I am a bit baffled that the most voted answer keeps spreading the over-simplified myth (or half-truth) of "everything being an object in JavaScript"; that is not exactly true, otherwise you will never study primitives, for example.
Try to do this:
const pippi = "pippi"
pippi.cat = "cat"
console.log(pippi.cat) // Will it work? Throw an error? Guess why again
Spoiler: the string is wrapped in a throwaway object for that specific operation on the second line, and then in the following one you are just going to access a property of the primitive which is not there (provided you did not play with String.prototype or the like), so you get undefined.
Array object inherits caller, constructor, length, and name properties from Function.prototype.
A JavaScript array is an object just like any other object, but JavaScript gives it special syntax.
arr[5] = "yo"
The above is syntactic sugar for
arr.insert(5,"yo")
which is how you would add stuff to a regular object. It's what is inside the insert method that changes the value of arr.length
See my implementation of a customArray type here: http://jsfiddle.net/vfm3vkxy/4/