Most of the tutorials that I've read on arrays in JavaScript (including w3schools and devguru) suggest that you can initialize an array with a certain length by passing an integer to the Array constructor using the var test = new Array(4);
syntax.
After using this syntax liberally in my js files, I ran one of the files through jsLint, and it freaked out:
Error: Problem at line 1 character 22: Expected ')' and instead saw '4'.
var test = new Array(4);
Problem at line 1 character 23: Expected ';' and instead saw ')'.
var test = new Array(4);
Problem at line 1 character 23: Expected an identifier and instead saw ')'.
After reading through jsLint's explanation of its behavior, it looks like jsLint doesn't really like the new Array()
syntax, and instead prefers []
when declaring arrays.
So I have a couple questions:
First, why? Am I running any risk by using the new Array()
syntax instead? Are there browser incompatibilities that I should be aware of?
And second, if I switch to the square bracket syntax, is there any way to declare an array and set its length all on one line, or do I have to do something like this:
var test = [];
test.length = 4;
This question is related to
javascript
arrays
jslint
The shortest:
let arr = [...Array(10)];
console.log(arr);
_x000D_
Here is another solution
var arr = Array.apply( null, { length: 4 } );
arr; // [undefined, undefined, undefined, undefined] (in Chrome)
arr.length; // 4
The first argument of apply()
is a this object binding, which we don't care about here, so we set it to null
.
Array.apply(..)
is calling the Array(..)
function and spreading out the { length: 3 }
object value as its arguments.
As explained above, using new Array(size)
is somewhat dangerous. Instead of using it directly, place it inside an "array creator function". You can easily make sure that this function is bug-free and you avoid the danger of calling new Array(size)
directly. Also, you can give it an optional default initial value. This createArray
function does exactly that:
function createArray(size, defaultVal) {
const arr = new Array(size);
if (defaultVal !== undefined) {
// optional default value
for (let i = 0; i < size; ++i) {
arr[i] = defaultVal;
}
}
return arr;
}
Assuming that Array's length is constant. In Javascript, This is what we do:
const intialArray = new Array(specify the value);
The array constructor has an ambiguous syntax, and JSLint just hurts your feelings after all.
Also, your example code is broken, the second var
statement will raise a SyntaxError
. You're setting the property length
of the array test
, so there's no need for another var
.
As far as your options go, array.length
is the only "clean" one. Question is, why do you need to set the size in the first place? Try to refactor your code to get rid of that dependency.
ES6 introduces Array.from
which lets you create an Array
from any "array-like" or iterables objects:
Array.from({length: 10}, (x, i) => i);
// [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
In this case {length: 10}
represents the minimal definition of an "array-like" object: an empty object with just a length
property defined.
Array.from
allows for a second argument to map over the resulting array.
In most answers it is recommended to fill
the array because otherwise "you can't iterate over it", but this is not true. You can iterate an empty array, just not with forEach
. While loops, for of loops and for i loops work fine.
const count = Array(5);
Does not work.
console.log('---for each loop:---');
count.forEach((empty, index) => {
console.log(`counting ${index}`);
});
These work:
console.log('---for of loop:---');
for (let [index, empty] of count.entries()) {
console.log(`counting for of loop ${index}`);
}
console.log('---for i loop:---');
for (let i = 0, il = count.length; i < il; ++i) {
console.log(`counting for i loop ${i}`);
}
console.log('---while loop:---');
let index = 0;
while (index < count.length) {
console.log(`counting while loop ${index}`);
index++;
}
Check this fiddle with the above examples.
Also angulars *ngFor
works fine with an empty array:
<li *ngFor="let empty of count; let i = index" [ngClass]="
<span>Counting with *ngFor {{i}}</span>
</li>
Please people don't give up your old habits just yet. There is a large difference in speed between allocating memory once then working with the entries in that array (as of old), and allocating it many times as an array grows (which is inevitably what the system does under the hood with other suggested methods).
None of this matters of course, until you want to do something cool with larger arrays. Then it does.
Seeing as there still seems to be no option in JS at the moment to set the initial capacity of an array, I use the following...
var newArrayWithSize = function(size) {
this.standard = this.standard||[];
for (var add = size-this.standard.length; add>0; add--) {
this.standard.push(undefined);// or whatever
}
return this.standard.slice(0,size);
}
There are tradeoffs involved:
standard
array does permanently reserve as much space as the largest array you have asked for.But if it fits with what you're doing there can be a payoff. Informal timing puts
for (var n=10000;n>0;n--) {var b = newArrayWithSize(10000);b[0]=0;}
at pretty speedy (about 50ms for the 10000 given that with n=1000000 it took about 5 seconds), and
for (var n=10000;n>0;n--) {
var b = [];for (var add=10000;add>0;add--) {
b.push(undefined);
}
}
at well over a minute (about 90 sec for the 10000 on the same chrome console, or about 2000 times slower). That won't just be the allocation, but also the 10000 pushes, for loop, etc..
var arr=[];
arr[5]=0;
alert("length="+arr.length); // gives 6
With ES2015 .fill()
you can now simply do:
// `n` is the size you want to initialize your array
// `0` is what the array will be filled with (can be any other value)
Array(n).fill(0)
Which is a lot more concise than Array.apply(0, new Array(n)).map(i => value)
It is possible to drop the 0
in .fill()
and run without arguments, which will fill the array with undefined
. (However, this will fail in Typescript)
You can set the array length by using array.length = youValue
So it would be
var myArray = [];
myArray.length = yourValue;
The reason you shouldn't use new Array
is demonstrated by this code:
var Array = function () {};
var x = new Array(4);
alert(x.length); // undefined...
Some other code could mess with the Array variable. I know it's a bit far fetched that anyone would write such code, but still...
Also, as Felix King said, the interface is a little inconsistent, and could lead to some very difficult-to-track-down bugs.
If you wanted an array with length = x, filled with undefined (as new Array(x)
would do), you could do this:
var x = 4;
var myArray = [];
myArray[x - 1] = undefined;
alert(myArray.length); // 4
I'm surprised there hasn't been a functional solution suggested that allows you to set the length in one line. The following is based on UnderscoreJS:
var test = _.map(_.range(4), function () { return undefined; });
console.log(test.length);
For reasons mentioned above, I'd avoid doing this unless I wanted to initialize the array to a specific value. It's interesting to note there are other libraries that implement range including Lo-dash and Lazy, which may have different performance characteristics.
[...Array(6)].map(x => 0);
// [0, 0, 0, 0, 0, 0]
OR
Array(6).fill(0);
// [0, 0, 0, 0, 0, 0]
Note: you can't loop empty slots i.e. Array(4).forEach(() => …)
OR
( typescript safe )
Array(6).fill(null).map((_, i) => i);
// [0, 1, 2, 3, 4, 5]
OR
Classic method using a function ( works in any browser )
function NewArray(size) {
var x = [];
for (var i = 0; i < size; ++i) {
x[i] = i;
}
return x;
}
var a = NewArray(10);
// [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
When creating a 2D array with the fill
intuitively should create new instances. But what actually going to happen is the same array will be stored as a reference.
var a = Array(3).fill([6]);
// [ [6], [6], [6] ]
a[0].push(9);
// [ [6, 9], [6, 9], [6, 9] ]
Solution
var a = [...Array(3)].map(x => []);
a[0].push(4, 2);
// [ [4, 2], [], [] ]
So a 3x2 Array will look something like this:
[...Array(3)].map(x => Array(2).fill(0));
// [ [0, 0], [0, 0], [0, 0] ]
function NArray(...dimensions) {
var index = 0;
function NArrayRec(dims) {
var first = dims[0], next = dims.slice().splice(1);
if(dims.length > 1)
return Array(dims[0]).fill(null).map((x, i) => NArrayRec(next ));
return Array(dims[0]).fill(null).map((x, i) => (index++));
}
return NArrayRec(dimensions);
}
var arr = NArray(3, 2, 4);
// [ [ [ 0, 1, 2, 3 ] , [ 4, 5, 6, 7] ],
// [ [ 8, 9, 10, 11] , [ 12, 13, 14, 15] ],
// [ [ 16, 17, 18, 19] , [ 20, 21, 22, 23] ] ]
var Chessboard = [...Array(8)].map((x, j) => {
return Array(8).fill(null).map((y, i) => {
return `${String.fromCharCode(65 + i)}${8 - j}`;
});
});
// [ [A8, B8, C8, D8, E8, F8, G8, H8],
// [A7, B7, C7, D7, E7, F7, G7, H7],
// [A6, B6, C6, D6, E6, F6, G6, H6],
// [A5, B5, C5, D5, E5, F5, G5, H5],
// [A4, B4, C4, D4, E4, F4, G4, H4],
// [A3, B3, C3, D3, E3, F3, G3, H3],
// [A2, B2, C2, D2, E2, F2, G2, H2],
// [A1, B1, C1, D1, E1, F1, G1, H1] ]
handy little method overload when working with math
function NewArray( size , method, linear )
{
method = method || ( i => i );
linear = linear || false;
var x = [];
for( var i = 0; i < size; ++i )
x[ i ] = method( linear ? i / (size-1) : i );
return x;
}
NewArray( 4 );
// [ 0, 1, 2, 3 ]
NewArray( 4, Math.sin );
// [ 0, 0.841, 0.909, 0.141 ]
NewArray( 4, Math.sin, true );
// [ 0, 0.327, 0.618, 0.841 ]
var pow2 = ( x ) => x * x;
NewArray( 4, pow2 );
// [ 0, 1, 4, 9 ]
NewArray( 4, pow2, true );
// [ 0, 0.111, 0.444, 1 ]
This will initialize the length property to 4:
var x = [,,,,];
(this was probably better as a comment, but got too long)
So, after reading this I was curious if pre-allocating was actually faster, because in theory it should be. However, this blog gave some tips advising against it http://www.html5rocks.com/en/tutorials/speed/v8/.
So still being unsure, I put it to the test. And as it turns out it seems to in fact be slower.
var time = Date.now();
var temp = [];
for(var i=0;i<100000;i++){
temp[i]=i;
}
console.log(Date.now()-time);
var time = Date.now();
var temp2 = new Array(100000);
for(var i=0;i<100000;i++){
temp2[i] = i;
}
console.log(Date.now()-time);
This code yields the following after a few casual runs:
$ node main.js
9
16
$ node main.js
8
14
$ node main.js
7
20
$ node main.js
9
14
$ node main.js
9
19
Array(5)
gives you an array with length 5 but no values, hence you can't iterate over it.
Array.apply(null, Array(5)).map(function () {})
gives you an array with length 5 and undefined as values, now it can be iterated over.
Array.apply(null, Array(5)).map(function (x, i) { return i; })
gives you an array with length 5 and values 0,1,2,3,4.
Array(5).forEach(alert)
does nothing, Array.apply(null, Array(5)).forEach(alert)
gives you 5 alerts
ES6
gives us Array.from
so now you can also use Array.from(Array(5)).forEach(alert)
If you want to initialize with a certain value, these are good to knows...
Array.from('abcde')
, Array.from('x'.repeat(5))
or Array.from({length: 5}, (v, i) => i) // gives [0, 1, 2, 3, 4]
Source: Stackoverflow.com