What's the most straightforward way to copy an ArrayBuffer object? - javascript

I'm working with ArrayBuffer objects, and I would like to duplicate them. While this is rather easy with actual pointers and memcpy, I couldn't find any straightforward way to do it in Javascript.
Right now, this is how I copy my ArrayBuffers:
function copy(buffer)
{
var bytes = new Uint8Array(buffer);
var output = new ArrayBuffer(buffer.byteLength);
var outputBytes = new Uint8Array(output);
for (var i = 0; i < bytes.length; i++)
outputBytes[i] = bytes[i];
return output;
}
Is there a prettier way?

I prefer the following method
function copy(src) {
var dst = new ArrayBuffer(src.byteLength);
new Uint8Array(dst).set(new Uint8Array(src));
return dst;
}

It appears that simply passing in the source dataview performs a copy:
var a = new Uint8Array([2,3,4,5]);
var b = new Uint8Array(a);
a[0] = 6;
console.log(a); // [6, 3, 4, 5]
console.log(b); // [2, 3, 4, 5]
Tested in FF 33 and Chrome 36.

ArrayBuffer is supposed to support slice (http://www.khronos.org/registry/typedarray/specs/latest/) so you can try,
buffer.slice(0);
which works in Chrome 18 but not Firefox 10 or 11. As for Firefox, you need to copy it manually. You can monkey patch the slice() in Firefox because the Chrome slice() will outperform a manual copy. This would look something like,
if (!ArrayBuffer.prototype.slice)
ArrayBuffer.prototype.slice = function (start, end) {
var that = new Uint8Array(this);
if (end == undefined) end = that.length;
var result = new ArrayBuffer(end - start);
var resultArray = new Uint8Array(result);
for (var i = 0; i < resultArray.length; i++)
resultArray[i] = that[i + start];
return result;
}
Then you can call,
buffer.slice(0);
to copy the array in both Chrome and Firefox.

Hmmm... if it's the Uint8Array you want to slice (which logically, it should be), this may work.
if (!Uint8Array.prototype.slice && 'subarray' in Uint8Array.prototype)
Uint8Array.prototype.slice = Uint8Array.prototype.subarray;

Faster and slightly more complicated version of chuckj's answer. Should use ~8x less copy operations on large Typed Arrays. Basically we copy as much 8-byte chunks as possible and then copy the remaining 0-7 bytes. This is especially useful in current version of IE, since it doesn't have slice method implemented for ArrayBuffer.
if (!ArrayBuffer.prototype.slice)
ArrayBuffer.prototype.slice = function (start, end) {
if (end == undefined) end = that.length;
var length = end - start;
var lengthDouble = Math.floor(length / Float64Array.BYTES_PER_ELEMENT);
// ArrayBuffer that will be returned
var result = new ArrayBuffer(length);
var that = new Float64Array(this, start, lengthDouble)
var resultArray = new Float64Array(result, 0, lengthDouble);
for (var i = 0; i < resultArray.length; i++)
resultArray[i] = that[i];
// copying over the remaining bytes
that = new Uint8Array(this, start + lengthDouble * Float64Array.BYTES_PER_ELEMENT)
resultArray = new Uint8Array(result, lengthDouble * Float64Array.BYTES_PER_ELEMENT);
for (var i = 0; i < resultArray.length; i++)
resultArray[i] = that[i];
return result;
}

Wrap a Buffer around the ArrayBuffer. This is shared memory and no copy is made. Then create a new Buffer from the wrapping Buffer. This will copy the data. Finally get a reference to the new Buffer's ArrayBuffer.
This is the most straighforward way I can find. The most efficient? Perhaps.
const wrappingBuffer = Buffer.from(arrayBuffer)
const copiedBuffer = Buffer.from(wrappingBuffer)
const copiedArrayBuffer = copiedBuffer.buffer

In some cases (like webaudio Audiobuffers) you only have a reference to the 2 arrays.
So if you have array1 as a float32Array and array2 as a float32Array,
you must do an element by element copy.
To do so you can use different methods.
var ib=z.inputBuffer.getChannelData(0);
var ob=z.outputBuffer.getChannelData(0);
this
ib.forEach((chd,i)=>ob[i]=chd);
or this nicer and probably faster
ob.set(ib);
That's because Array.set populates an existing array with multiple data (even from another array)

Some of the operations above only do "shallow" copies. When working with workers and transferable arrays, you need to do a deep copy.
function copyTypedArray(original, deep){
var copy;
var kon = original.constructor;
if(deep){
var len = original.length;
copy = new kon(len);
for (var k=len; --k;) {
copy[k] = original[k];
}
} else {
var sBuf = original.buffer;
copy = new kon(sBuf);
copy.set(original);
}
return copy;
}
HINT (for the confused): Typed Arrays contain an ArrayBuffer, which can be obtained via the "buffer" property.
var arr = new Float32Array(8);
arr.buffer <-- this is an ArrayBuffer

If you're in the browser you can do:
const copy = structuredClone(buffer);

Related

Nested for loops are not generating random values for each iteration [duplicate]

I was toying a bit and was trying to instantiate a new array of length x, where all elements of that array were initialized to a value y:
var arr = new Array(x).fill(y);
This works well if the value of y is anything other than an object.
Meaning that if y is an object, the following is true:
var arr = new Array(2).fill({});
arr[0] === arr[1]; //is true;
arr[0].test = 'string';
arr[1].test === 'string'; //is also true;
Is there any way to state that a new object should be created for each element while using the fill-function? Or should I just convert it to a loop?
You can first fill the array with any value (e.g. undefined), and then you will be able to use map:
var arr = new Array(2).fill().map(u => ({}));
var arr = new Array(2).fill().map(Object);
The accepted answer is good and would work in 90% of cases.
But if you are making high-performance JS application, and if you work with big/huge arrays, Array.map(..) creates big overload in both - memory and processor use, as it creates a copy of an array.
I recommend to use the classic for loop:
a = new Array(ARRAY_SIZE);
for (var i = 0; i < ARRAY_SIZE; i++) {
a[i] = [];
}
// or it's one line alternative
for (var i = 0, a = []; i < ARRAY_SIZE; a[i++] = []);
I tested six alternatives and got this:
Array.map(), as proposed above (11x times!!! slower):
a = new Array(ARRAY_SIZE).fill().map(u => { return []; });
for loop, the best one (fastest):
// Standard multi-line way
a = new Array(ARRAY_SIZE);
for (var i = 0; i < ARRAY_SIZE; i++) {
a[i] = [];
}
// One line syntax
for (var i = 0, a = []; i < ARRAY_SIZE; a[i++] = []);
forEach (6x time slower):
a = new Array(ARRAY_SIZE).fill();
a.forEach((val, i) => {
a[i] = [];
})
[UPDATE 2020-08-27] One more way proposed by Ilias Karim below
Array.from (30x times!!! slower) - apparently worse in terms of performance, despite the nicest syntax :(
a = Array.from({ length: ARRAY_SIZE }, () => []);
[..Array(..)] (5x times!!! slower)
a = [...Array(ARRAY_SIZE)].map(_=>([]))
Array.push(..), second place in terms of performance (2x times!!! slower)
let a = [], total = ARRAY_SIZE;
while(total--) a.push([]);
PS. I used this fiddle for tests.
One performant solution:
Array.from({ length: 5 }, () => new Object())
Shortest Possable:
let node = [...Array(2)].map(_=>({}))
console.log(node)
Ilias Karim's answer is most excellent. I just did the following:
a = Array.from({length:l}, () => new Array(c).fill(prefix));
to create a pre-filled 2D array of the specified size, l by c, filled with prefix. Now my code can fill in the slots in the 2D matrix that need non-prefix values.
I wrote a blog post about this: http://www.samhenderson.xyz/posts/12
But the TLDR is that if you want to avoid chaining multiple function e.g. fill, map. And want to avoid writing a loop, then you can use:
const array = Array.from({ length: 2 },()=>({}))
For an array of arrays:
const array = Array.from({ length: 2 },()=>([]))
To add to answers that explain the aliasing issue and how to solve it, here's a handy function that can be used to create arrays with cleaner syntax for the caller:
const array = (length, fill) =>
[...Array(length)].map((_, i) =>
typeof fill === "function" ? fill(i) : fill
);
// usage:
const a = array(3, i => array(3, j => [i, j]));
a[0][0][0] = -42;
console.log(a);
Note that you still need to use a callback function for non-primitive values. This is actually a feature as it exposes the index and lets you provide arbitrary logic to fill the element. If you're concerned about accidentally passing a non-primitive, non-function object as the fill value, you can throw an error.
If you really want to be able to pass an object directly and have it copied under the hood, here's an adjustment that pretty much prohibits aliasing:
const array = (length, fill) =>
[...Array(length)].map((x, i) =>
typeof fill === "function" ? fill(i) :
typeof fill === "object" ? _.cloneDeep(fill) : fill
);
// usage:
const a = array(2, array(2, {foo: 3}));
a[0][0].foo = 42;
console.log(a);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script>
In general, I suggest avoiding .fill() almost entirely in favor of the spread syntax [...Array()] because it's easy to forget about the aliasing behavior and wind up with a frustrating bug.
If speed matters, use a traditional for loop:
const array = (length, fill) => {
const a = [];
for (let i = 0; i < length; i++) {
a[i] = typeof fill === "function" ? fill(i) : fill;
}
return a;
};
// usage:
const a = array(2, () => array(2, Object));
a[0][0].foo = 42;
console.log(a);

What is intended behavior for += with 2D arrays in JavaScript? [duplicate]

I was toying a bit and was trying to instantiate a new array of length x, where all elements of that array were initialized to a value y:
var arr = new Array(x).fill(y);
This works well if the value of y is anything other than an object.
Meaning that if y is an object, the following is true:
var arr = new Array(2).fill({});
arr[0] === arr[1]; //is true;
arr[0].test = 'string';
arr[1].test === 'string'; //is also true;
Is there any way to state that a new object should be created for each element while using the fill-function? Or should I just convert it to a loop?
You can first fill the array with any value (e.g. undefined), and then you will be able to use map:
var arr = new Array(2).fill().map(u => ({}));
var arr = new Array(2).fill().map(Object);
The accepted answer is good and would work in 90% of cases.
But if you are making high-performance JS application, and if you work with big/huge arrays, Array.map(..) creates big overload in both - memory and processor use, as it creates a copy of an array.
I recommend to use the classic for loop:
a = new Array(ARRAY_SIZE);
for (var i = 0; i < ARRAY_SIZE; i++) {
a[i] = [];
}
// or it's one line alternative
for (var i = 0, a = []; i < ARRAY_SIZE; a[i++] = []);
I tested six alternatives and got this:
Array.map(), as proposed above (11x times!!! slower):
a = new Array(ARRAY_SIZE).fill().map(u => { return []; });
for loop, the best one (fastest):
// Standard multi-line way
a = new Array(ARRAY_SIZE);
for (var i = 0; i < ARRAY_SIZE; i++) {
a[i] = [];
}
// One line syntax
for (var i = 0, a = []; i < ARRAY_SIZE; a[i++] = []);
forEach (6x time slower):
a = new Array(ARRAY_SIZE).fill();
a.forEach((val, i) => {
a[i] = [];
})
[UPDATE 2020-08-27] One more way proposed by Ilias Karim below
Array.from (30x times!!! slower) - apparently worse in terms of performance, despite the nicest syntax :(
a = Array.from({ length: ARRAY_SIZE }, () => []);
[..Array(..)] (5x times!!! slower)
a = [...Array(ARRAY_SIZE)].map(_=>([]))
Array.push(..), second place in terms of performance (2x times!!! slower)
let a = [], total = ARRAY_SIZE;
while(total--) a.push([]);
PS. I used this fiddle for tests.
One performant solution:
Array.from({ length: 5 }, () => new Object())
Shortest Possable:
let node = [...Array(2)].map(_=>({}))
console.log(node)
Ilias Karim's answer is most excellent. I just did the following:
a = Array.from({length:l}, () => new Array(c).fill(prefix));
to create a pre-filled 2D array of the specified size, l by c, filled with prefix. Now my code can fill in the slots in the 2D matrix that need non-prefix values.
I wrote a blog post about this: http://www.samhenderson.xyz/posts/12
But the TLDR is that if you want to avoid chaining multiple function e.g. fill, map. And want to avoid writing a loop, then you can use:
const array = Array.from({ length: 2 },()=>({}))
For an array of arrays:
const array = Array.from({ length: 2 },()=>([]))
To add to answers that explain the aliasing issue and how to solve it, here's a handy function that can be used to create arrays with cleaner syntax for the caller:
const array = (length, fill) =>
[...Array(length)].map((_, i) =>
typeof fill === "function" ? fill(i) : fill
);
// usage:
const a = array(3, i => array(3, j => [i, j]));
a[0][0][0] = -42;
console.log(a);
Note that you still need to use a callback function for non-primitive values. This is actually a feature as it exposes the index and lets you provide arbitrary logic to fill the element. If you're concerned about accidentally passing a non-primitive, non-function object as the fill value, you can throw an error.
If you really want to be able to pass an object directly and have it copied under the hood, here's an adjustment that pretty much prohibits aliasing:
const array = (length, fill) =>
[...Array(length)].map((x, i) =>
typeof fill === "function" ? fill(i) :
typeof fill === "object" ? _.cloneDeep(fill) : fill
);
// usage:
const a = array(2, array(2, {foo: 3}));
a[0][0].foo = 42;
console.log(a);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script>
In general, I suggest avoiding .fill() almost entirely in favor of the spread syntax [...Array()] because it's easy to forget about the aliasing behavior and wind up with a frustrating bug.
If speed matters, use a traditional for loop:
const array = (length, fill) => {
const a = [];
for (let i = 0; i < length; i++) {
a[i] = typeof fill === "function" ? fill(i) : fill;
}
return a;
};
// usage:
const a = array(2, () => array(2, Object));
a[0][0].foo = 42;
console.log(a);

How to append bytes, multi-bytes and buffer to ArrayBuffer in javascript?

Javascript ArrayBuffer or TypedArrays dont have any kind of appendByte(), appendBytes(), or appendBuffer() methods. So if I want to fill an ArrayBuffer one value at a time, how do I do it?
var firstVal = 0xAB; // 1 byte
var secondVal = 0x3D7F // 2 bytes
var anotherUint8Array = someArr;
var buffer = new ArrayBuffer(); // I don't know the length yet
var bufferArr = new UInt8Array(buffer);
// following methods do not exist. What are the alternatives for each??
bufferArr.appendByte(firstVal);
bufferArr.appendBytes(secondVal);
bufferArr.appendBuffer(anotherUint8Array);
You can create a new TypedArray with a new ArrayBuffer, but you can't change the size of an existing buffer
function concatTypedArrays(a, b) { // a, b TypedArray of same type
var c = new (a.constructor)(a.length + b.length);
c.set(a, 0);
c.set(b, a.length);
return c;
}
Now can do
var a = new Uint8Array(2),
b = new Uint8Array(3);
a[0] = 1; a[1] = 2;
b[0] = 3; b[1] = 4;
concatTypedArrays(a, b); // [1, 2, 3, 4, 0] Uint8Array length 5
If you want to use different types, go via Uint8Array as the smallest unit is a byte, i.e.
function concatBuffers(a, b) {
return concatTypedArrays(
new Uint8Array(a.buffer || a),
new Uint8Array(b.buffer || b)
).buffer;
}
This means .length will work as expected, you could now convert this to your typed array of choice (make sure it's a type that would accept the .byteLength of the buffer though)
From here, you could now implement any method you like for concatenating your data, e.g.
function concatBytes(ui8a, byte) {
var b = new Uint8Array(1);
b[0] = byte;
return concatTypedArrays(ui8a, b);
}
var u8 = new Uint8Array(0);
u8 = concatBytes(u8, 0x80); // [128]
Paul's answer allows you to concatenate one TypedArray to an existing TypedArray. In ES6, you can use the following function to concatenate multiple TypedArrays:
function concatenate(resultConstructor, ...arrays) {
let totalLength = 0;
for (const arr of arrays) {
totalLength += arr.length;
}
const result = new resultConstructor(totalLength);
let offset = 0;
for (const arr of arrays) {
result.set(arr, offset);
offset += arr.length;
}
return result;
}
const ta = concatenate(Uint8Array,
Uint8Array.of(1, 2), Uint8Array.of(3, 4));
console.log(ta); // Uint8Array [1, 2, 3, 4]
console.log(ta.buffer.byteLength); // 4
To append a new byte is:
const byte = 3;
concatenate(Uint8Array, Uint8Array.of(1, 2), Uint8Array.of(byte));
This method is found in ExploringJS.

Fastest way to duplicate an array in JavaScript - slice vs. 'for' loop

In order to duplicate an array in JavaScript: Which of the following is faster to use?
Slice method
var dup_array = original_array.slice();
For loop
for(var i = 0, len = original_array.length; i < len; ++i)
dup_array[i] = original_array[i];
I know both ways do only a shallow copy: if original_array contains references to objects, objects won't be cloned, but only the references will be copied, and therefore both arrays will have references to the same objects.
But this is not the point of this question.
I'm asking only about speed.
There are at least 6 (!) ways to clone an array:
loop
slice
Array.from()
concat
spread syntax (FASTEST)
map A.map(function(e){return e;});
There has been a huuuge BENCHMARKS thread, providing following information:
for blink browsers slice() is the fastest method, concat() is a bit slower, and while loop is 2.4x slower.
for other browsers while loop is the fastest method, since those browsers don't have internal optimizations for slice and concat.
This remains true in Jul 2016.
Below are simple scripts that you can copy-paste into your browser's console and run several times to see the picture. They output milliseconds, lower is better.
while loop
n = 1000*1000;
start = + new Date();
a = Array(n);
b = Array(n);
i = a.length;
while(i--) b[i] = a[i];
console.log(new Date() - start);
slice
n = 1000*1000;
start = + new Date();
a = Array(n);
b = a.slice();
console.log(new Date() - start);
Please note that these methods will clone the Array object itself, array contents however are copied by reference and are not deep cloned.
origAr == clonedArr //returns false
origAr[0] == clonedArr[0] //returns true
Technically slice is the fastest way. However, it is even faster if you add the 0 begin index.
myArray.slice(0);
is faster than
myArray.slice();
https://jsben.ch/F0SZ3
what about es6 way?
arr2 = [...arr1];
Easiest way to deep clone Array or Object:
var dup_array = JSON.parse(JSON.stringify(original_array))
🏁 Fastest Way to Clone an Array
I made this very plain utility function to test the time that it takes to clone an array. It is not 100% reliable however it can give you a bulk idea as for how long it takes to clone an existing array:
function clone(fn) {
const arr = [...Array(1000000)];
console.time('timer');
fn(arr);
console.timeEnd('timer');
}
And tested different approach:
1) 5.79ms -> clone(arr => Object.values(arr));
2) 7.23ms -> clone(arr => [].concat(arr));
3) 9.13ms -> clone(arr => arr.slice());
4) 24.04ms -> clone(arr => { const a = []; for (let val of arr) { a.push(val); } return a; });
5) 30.02ms -> clone(arr => [...arr]);
6) 39.72ms -> clone(arr => JSON.parse(JSON.stringify(arr)));
7) 99.80ms -> clone(arr => arr.map(i => i));
8) 259.29ms -> clone(arr => Object.assign([], arr));
9) Maximum call stack size exceeded -> clone(arr => Array.of(...arr));
UPDATE:
Tests were made back in 2018, so today most likely you'll get different result with current browsers.
Out of all of those, the only way to deep clone an array is by using JSON.parse(JSON.stringify(arr)).
That said, do not use the above if your array might include functions as it will return null.Thank you #GilEpshtain for this update.
var cloned_array = [].concat(target_array);
I put together a quick demo: http://jsbin.com/agugo3/edit
My results on Internet Explorer 8 are 156, 782, and 750, which would indicate slice is much faster in this case.
a.map(e => e) is another alternative for this job. As of today .map() is very fast (almost as fast as .slice(0)) in Firefox, but not in Chrome.
On the other hand, if an array is multi-dimensional, since arrays are objects and objects are reference types, none of the slice or concat methods will be a cure... So one proper way of cloning an array is an invention of Array.prototype.clone() as follows.
Array.prototype.clone = function(){
return this.map(e => Array.isArray(e) ? e.clone() : e);
};
var arr = [ 1, 2, 3, 4, [ 1, 2, [ 1, 2, 3 ], 4 , 5], 6 ],
brr = arr.clone();
brr[4][2][1] = "two";
console.log(JSON.stringify(arr));
console.log(JSON.stringify(brr));
Fastest way to clone an Array of Objects will be using spread operator
var clonedArray=[...originalArray]
or
var clonedArray = originalArray.slice(0); //with 0 index it's little bit faster than normal slice()
but the objects inside that cloned array will still pointing at the old memory location. hence change to clonedArray objects will also change the orignalArray. So
var clonedArray = originalArray.map(({...ele}) => {return ele})
this will not only create new array but also the objects will be cloned to.
disclaimer if you are working with nested object in that case spread operator will work as SHALLOW CLONE. At that point better to use
var clonedArray=JSON.parse(JSON.stringify(originalArray));
Take a look at: link. It's not about speed, but comfort. Besides as you can see you can only use slice(0) on primitive types.
To make an independent copy of an array rather than a copy of the refence to it, you can use the array slice method.
Example:
To make an independent copy of an array rather than a copy of the refence to it, you can use the array slice method.
var oldArray = ["mip", "map", "mop"];
var newArray = oldArray.slice();
To copy or clone an object :
function cloneObject(source) {
for (i in source) {
if (typeof source[i] == 'source') {
this[i] = new cloneObject(source[i]);
}
else{
this[i] = source[i];
}
}
}
var obj1= {bla:'blabla',foo:'foofoo',etc:'etc'};
var obj2= new cloneObject(obj1);
Source: link
ECMAScript 2015 way with the Spread operator:
Basic examples:
var copyOfOldArray = [...oldArray]
var twoArraysBecomeOne = [...firstArray, ..seccondArray]
Try in the browser console:
var oldArray = [1, 2, 3]
var copyOfOldArray = [...oldArray]
console.log(oldArray)
console.log(copyOfOldArray)
var firstArray = [5, 6, 7]
var seccondArray = ["a", "b", "c"]
var twoArraysBecomOne = [...firstArray, ...seccondArray]
console.log(twoArraysBecomOne);
References
6 Great Uses of the Spread Operator
Spread syntax
As #Dan said "This answer becomes outdated fast. Use benchmarks to check the actual situation", there is one specific answer from jsperf that has not had an answer for itself: while:
var i = a.length;
while(i--) { b[i] = a[i]; }
had 960,589 ops/sec with the runnerup a.concat() at 578,129 ops/sec, which is 60%.
This is the lastest Firefox (40) 64 bit.
#aleclarson created a new, more reliable benchmark.
Benchmark time!
function log(data) {
document.getElementById("log").textContent += data + "\n";
}
benchmark = (() => {
time_function = function(ms, f, num) {
var z = 0;
var t = new Date().getTime();
for (z = 0;
((new Date().getTime() - t) < ms); z++)
f(num);
return (z)
}
function clone1(arr) {
return arr.slice(0);
}
function clone2(arr) {
return [...arr]
}
function clone3(arr) {
return [].concat(arr);
}
Array.prototype.clone = function() {
return this.map(e => Array.isArray(e) ? e.clone() : e);
};
function clone4(arr) {
return arr.clone();
}
function benchmark() {
function compare(a, b) {
if (a[1] > b[1]) {
return -1;
}
if (a[1] < b[1]) {
return 1;
}
return 0;
}
funcs = [clone1, clone2, clone3, clone4];
results = [];
funcs.forEach((ff) => {
console.log("Benchmarking: " + ff.name);
var s = time_function(2500, ff, Array(1024));
results.push([ff, s]);
console.log("Score: " + s);
})
return results.sort(compare);
}
return benchmark;
})()
log("Starting benchmark...\n");
res = benchmark();
console.log("Winner: " + res[0][0].name + " !!!");
count = 1;
res.forEach((r) => {
log((count++) + ". " + r[0].name + " score: " + Math.floor(10000 * r[1] / res[0][1]) / 100 + ((count == 2) ? "% *winner*" : "% speed of winner.") + " (" + Math.round(r[1] * 100) / 100 + ")");
});
log("\nWinner code:\n");
log(res[0][0].toString());
<textarea rows="50" cols="80" style="font-size: 16; resize:none; border: none;" id="log"></textarea>
The benchmark will run for 10s since you click the button.
My results:
Chrome (V8 engine):
1. clone1 score: 100% *winner* (4110764)
2. clone3 score: 74.32% speed of winner. (3055225)
3. clone2 score: 30.75% speed of winner. (1264182)
4. clone4 score: 21.96% speed of winner. (902929)
Firefox (SpiderMonkey Engine):
1. clone1 score: 100% *winner* (8448353)
2. clone3 score: 16.44% speed of winner. (1389241)
3. clone4 score: 5.69% speed of winner. (481162)
4. clone2 score: 2.27% speed of winner. (192433)
Winner code:
function clone1(arr) {
return arr.slice(0);
}
Winner engine:
SpiderMonkey (Mozilla/Firefox)
It depends on the browser. If you look in the blog post Array.prototype.slice vs manual array creation, there is a rough guide to performance of each:
Results:
There is a much cleaner solution:
var srcArray = [1, 2, 3];
var clonedArray = srcArray.length === 1 ? [srcArray[0]] : Array.apply(this, srcArray);
The length check is required, because the Array constructor behaves differently when it is called with exactly one argument.
Remember .slice() won't work for two-dimensional arrays. You'll need a function like this:
function copy(array) {
return array.map(function(arr) {
return arr.slice();
});
}
It depends on the length of the array. If the array length is <= 1,000,000, the slice and concat methods are taking approximately the same time. But when you give a wider range, the concat method wins.
For example, try this code:
var original_array = [];
for(var i = 0; i < 10000000; i ++) {
original_array.push( Math.floor(Math.random() * 1000000 + 1));
}
function a1() {
var dup = [];
var start = Date.now();
dup = original_array.slice();
var end = Date.now();
console.log('slice method takes ' + (end - start) + ' ms');
}
function a2() {
var dup = [];
var start = Date.now();
dup = original_array.concat([]);
var end = Date.now();
console.log('concat method takes ' + (end - start) + ' ms');
}
function a3() {
var dup = [];
var start = Date.now();
for(var i = 0; i < original_array.length; i ++) {
dup.push(original_array[i]);
}
var end = Date.now();
console.log('for loop with push method takes ' + (end - start) + ' ms');
}
function a4() {
var dup = [];
var start = Date.now();
for(var i = 0; i < original_array.length; i ++) {
dup[i] = original_array[i];
}
var end = Date.now();
console.log('for loop with = method takes ' + (end - start) + ' ms');
}
function a5() {
var dup = new Array(original_array.length)
var start = Date.now();
for(var i = 0; i < original_array.length; i ++) {
dup.push(original_array[i]);
}
var end = Date.now();
console.log('for loop with = method and array constructor takes ' + (end - start) + ' ms');
}
a1();
a2();
a3();
a4();
a5();
If you set the length of original_array to 1,000,000, the slice method and concat method are taking approximately the same time (3-4 ms, depending on the random numbers).
If you set the length of original_array to 10,000,000, then the slice method takes over 60 ms and the concat method takes over 20 ms.
In ES6, you can simply utilize the Spread syntax.
Example:
let arr = ['a', 'b', 'c'];
let arr2 = [...arr];
Please note that the spread operator generates a completely new array, so modifying one won't affect the other.
Example:
arr2.push('d') // becomes ['a', 'b', 'c', 'd']
console.log(arr) // while arr retains its values ['a', 'b', 'c']
A simple solution:
original = [1,2,3]
cloned = original.map(x=>x)
const arr = ['1', '2', '3'];
// Old way
const cloneArr = arr.slice();
// ES6 way
const cloneArrES6 = [...arr];
// But problem with 3rd approach is that if you are using muti-dimensional
// array, then only first level is copied
const nums = [
[1, 2],
[10],
];
const cloneNums = [...nums];
// Let's change the first item in the first nested item in our cloned array.
cloneNums[0][0] = '8';
console.log(cloneNums);
// [ [ '8', 2 ], [ 10 ], [ 300 ] ]
// NOOooo, the original is also affected
console.log(nums);
// [ [ '8', 2 ], [ 10 ], [ 300 ] ]
So, in order to avoid these scenarios to happen, use
const arr = ['1', '2', '3'];
const cloneArr = Array.from(arr);
There were several ways to clone an array. Basically, Cloning was categorized in two ways:
Shallow copy
Deep copy
Shallow copies only cover the 1st level of the array and the rest are
referenced. If you want a true copy of nested elements in the arrays, you’ll need a
deep clone.
Example :
const arr1 = [1,2,3,4,5,6,7]
// Normal Array (shallow copy is enough)
const arr2 = [1,2,3,[4],[[5]],6,7]
// Nested Array (Deep copy required)
Approach 1 : Using (...)Spread Operator (Shallow copy enough)
const newArray = [...arr1] // [1,2,3,4,5,6,7]
Approach 2 : Using Array builtIn Slice method (Deep copy)
const newArray = arr1.slice() // [1,2,3,4,5,6,7]
Approach 3 : Using Array builtIn Concat method (Deep a copy)
const newArray = [].concat(arr1) // [1,2,3,4,5,6,7]
Approach 4 : Using JSON.stringify/parse. (Deep a copy & fastest)
const newArray = JSON.parse(JSON.stringify(arr2));) // [1,2,3,[4],[[5]],6,7]
Approach 5: Using own recursive function or using loadash's __.cloneDeep method. (Deep copy)
Fast ways to duplicate an array in JavaScript in Order:
#1: array1copy = [...array1];
#2: array1copy = array1.slice(0);
#3: array1copy = array1.slice();
If your array objects contain some JSON-non-serializable content (functions, Number.POSITIVE_INFINITY, etc.) better to use
array1copy = JSON.parse(JSON.stringify(array1))
You can follow this code. Immutable way array clone. This is the perfect way to array cloning
const array = [1, 2, 3, 4]
const newArray = [...array]
newArray.push(6)
console.log(array)
console.log(newArray)
If you want a REAL cloned object/array in JS with cloned references of all attributes and sub-objects:
export function clone(arr) {
return JSON.parse(JSON.stringify(arr))
}
ALL other operations do not create clones, because they just change the base address of the root element, not of the included objects.
Except you traverse recursive through the object-tree.
For a simple copy, these are OK. For storage address relevant operations I suggest (and in most all other cases, because this is fast!) to type convert into string and back in a complete new object.
If you are taking about slice it is used to copy elements from an array and create a clone with same no. of elements or less no. of elements.
var arr = [1, 2, 3 , 4, 5];
function slc() {
var sliced = arr.slice(0, 5);
// arr.slice(position to start copying master array , no. of items in new array)
console.log(sliced);
}
slc(arr);

Most efficient way to create a zero filled JavaScript array?

What is the most efficient way to create an arbitrary length zero filled array in JavaScript?
ES6 introduces Array.prototype.fill. It can be used like this:
new Array(len).fill(0);
Not sure if it's fast, but I like it because it's short and self-describing.
It's still not in IE (check compatibility), but there's a polyfill available.
Although this is an old thread, I wanted to add my 2 cents to it. Not sure how slow/fast this is, but it's a quick one liner. Here is what I do:
If I want to pre-fill with a number:
Array.apply(null, Array(5)).map(Number.prototype.valueOf,0);
// [0, 0, 0, 0, 0]
If I want to pre-fill with a string:
Array.apply(null, Array(3)).map(String.prototype.valueOf,"hi")
// ["hi", "hi", "hi"]
Other answers have suggested:
new Array(5+1).join('0').split('')
// ["0", "0", "0", "0", "0"]
but if you want 0 (the number) and not "0" (zero inside a string), you can do:
new Array(5+1).join('0').split('').map(parseFloat)
// [0, 0, 0, 0, 0]
In short
Fastest solution:
let a = new Array(n); for (let i=0; i<n; ++i) a[i] = 0;
Shortest (handy) solution (3x slower for small arrays, slightly slower for big (slowest on Firefox))
Array(n).fill(0)
Details
Today 2020.06.09 I perform tests on macOS High Sierra 10.13.6 on browsers Chrome 83.0, Firefox 77.0, and Safari 13.1. I test chosen solutions for two test cases
small array - with 10 elements - you can perform test HERE
big arrays - with 1M elements - you can perform test HERE
Conclusions
solution based on new Array(n)+for (N) is fastest solution for small arrays and big arrays (except Chrome but still very fast there) and it is recommended as fast cross-browser solution
solution based on new Float32Array(n) (I) returns non typical array (e.g. you cannot call push(..) on it) so I not compare its results with other solutions - however this solution is about 10-20x faster than other solutions for big arrays on all browsers
solutions based on for (L,M,N,O) are fast for small arrays
solutions based on fill (B,C) are fast on Chrome and Safari but surprisingly slowest on Firefox for big arrays. They are medium fast for small arrays
solution based on Array.apply (P) throws error for big arrays
function P(n) {
return Array.apply(null, Array(n)).map(Number.prototype.valueOf,0);
}
try {
P(1000000);
} catch(e) {
console.error(e.message);
}
Code and example
Below code presents solutions used in measurements
function A(n) {
return [...new Array(n)].fill(0);
}
function B(n) {
return new Array(n).fill(0);
}
function C(n) {
return Array(n).fill(0);
}
function D(n) {
return Array.from({length: n}, () => 0);
}
function E(n) {
return [...new Array(n)].map(x => 0);
}
// arrays with type
function F(n) {
return Array.from(new Int32Array(n));
}
function G(n) {
return Array.from(new Float32Array(n));
}
function H(n) {
return Array.from(new Float64Array(n)); // needs 2x more memory than float32
}
function I(n) {
return new Float32Array(n); // this is not typical array
}
function J(n) {
return [].slice.apply(new Float32Array(n));
}
// Based on for
function K(n) {
let a = [];
a.length = n;
let i = 0;
while (i < n) {
a[i] = 0;
i++;
}
return a;
}
function L(n) {
let a=[]; for(let i=0; i<n; i++) a[i]=0;
return a;
}
function M(n) {
let a=[]; for(let i=0; i<n; i++) a.push(0);
return a;
}
function N(n) {
let a = new Array(n); for (let i=0; i<n; ++i) a[i] = 0;
return a;
}
function O(n) {
let a = new Array(n); for (let i=n; i--;) a[i] = 0;
return a;
}
// other
function P(n) {
return Array.apply(null, Array(n)).map(Number.prototype.valueOf,0);
}
function Q(n) {
return "0".repeat( n ).split("").map( parseFloat );
}
function R(n) {
return new Array(n+1).join('0').split('').map(parseFloat)
}
// ---------
// TEST
// ---------
[A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R].forEach(f => {
let a = f(10);
console.log(`${f.name} length=${a.length}, arr[0]=${a[0]}, arr[9]=${a[9]}`)
});
This snippets only present used codes
Example results for Chrome:
Elegant way to fill an array with precomputed values
Here is another way to do it using ES6 that nobody has mentioned so far:
> Array.from(Array(3), () => 0)
< [0, 0, 0]
It works by passing a map function as the second parameter of Array.from.
In the example above, the first parameter allocates an array of 3 positions filled with the value undefined and then the lambda function maps each one of them to the value 0.
Although Array(len).fill(0) is shorter, it doesn't work if you need to fill the array by doing some computation first (I know the question didn't ask for it, but a lot of people end up here looking for this).
For instance, if you need an array with 10 random numbers:
> Array.from(Array(10), () => Math.floor(10 * Math.random()))
< [3, 6, 8, 1, 9, 3, 0, 6, 7, 1]
It's more concise (and elegant) than the equivalent:
const numbers = Array(10);
for (let i = 0; i < numbers.length; i++) {
numbers[i] = Math.round(10 * Math.random());
}
This method can also be used to generate sequences of numbers by taking advantage of the index parameter provided in the callback:
> Array.from(Array(10), (d, i) => i)
< [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Bonus answer: fill an array using String repeat()
Since this answer is getting a good deal of attention, I also wanted to show this cool trick. Although not as useful as my main answer, will introduce the still not very known, but very useful String repeat() method. Here's the trick:
> "?".repeat(10).split("").map(() => Math.floor(10 * Math.random()))
< [5, 6, 3, 5, 0, 8, 2, 7, 4, 1]
Cool, huh? repeat() is a very useful method to create a string that is the repetition of the original string a certain number of times. After that, split() creates an array for us, which is then map()ped to the values we want. Breaking it down in steps:
> "?".repeat(10)
< "??????????"
> "?".repeat(10).split("")
< ["?", "?", "?", "?", "?", "?", "?", "?", "?", "?"]
> "?".repeat(10).split("").map(() => Math.floor(10 * Math.random()))
< [5, 6, 3, 5, 0, 8, 2, 7, 4, 1]
The already mentioned ES 6 fill method takes care of this nicely. Most modern desktop browsers already support the required Array prototype methods as of today (Chromium, FF, Edge and Safari) [1]. You can look up details on MDN. A simple usage example is
a = new Array(10).fill(0);
Given the current browser support you should be cautious to use this unless you are sure your audience uses modern Desktop browsers.
Note added August 2013, updated February 2015: The answer below from 2009 relates to JavaScript's generic Array type. It doesn't relate to the newer typed arrays defined in ES2015 [and available now in many browsers], like Int32Array and such. Also note that ES2015 adds a fill method to both Arrays and typed arrays, which is likely to be the most efficient way to fill them...
Also, it can make a big difference to some implementations how you create the array. Chrome's V8 engine, in particular, tries to use a highly-efficient, contiguous-memory array if it thinks it can, shifting to the object-based array only when necessary.
With most languages, it would be pre-allocate, then zero-fill, like this:
function newFilledArray(len, val) {
var rv = new Array(len);
while (--len >= 0) {
rv[len] = val;
}
return rv;
}
But, JavaScript arrays aren't really arrays, they're key/value maps just like all other JavaScript objects, so there's no "pre-allocate" to do (setting the length doesn't allocate that many slots to fill), nor is there any reason to believe that the benefit of counting down to zero (which is just to make the comparison in the loop fast) isn't outweighed by adding the keys in reverse order when the implementation may well have optimized their handling of the keys related to arrays on the theory you'll generally do them in order.
In fact, Matthew Crumley pointed out that counting down is markedly slower on Firefox than counting up, a result I can confirm — it's the array part of it (looping down to zero is still faster than looping up to a limit in a var). Apparently adding the elements to the array in reverse order is a slow op on Firefox. In fact, the results vary quite a bit by JavaScript implementation (which isn't all that surprising). Here's a quick and dirty test page (below) for browser implementations (very dirty, doesn't yield during tests, so provides minimal feedback and will run afoul of script time limits). I recommend refreshing between tests; FF (at least) slows down on repeated tests if you don't.
The fairly complicated version that uses Array#concat is faster than a straight init on FF as of somewhere between 1,000 and 2,000 element arrays. On Chrome's V8 engine, though, straight init wins out every time...
Here's a test:
const tests = [
{
name: "downpre",
total: 0,
desc: "Count down, pre-decrement",
func: makeWithCountDownPre
},
{
name: "downpost",
total: 0,
desc: "Count down, post-decrement",
func: makeWithCountDownPost
},
{
name: "up",
total: 0,
desc: "Count up (normal)",
func: makeWithCountUp
},
{
name: "downandup",
total: 0,
desc: "Count down (for loop) and up (for filling)",
func: makeWithCountDownArrayUp
},
{
name: "concat",
total: 0,
desc: "Concat",
func: makeWithConcat
}
];
const q = sel => document.querySelector(sel);
let markup = "";
for (const {name, desc} of tests) {
markup += `
<div><input type="checkbox" id="chk_${name}" checked>
<label for="chk_${name}">${desc}</label></div>`;
}
q("#checkboxes").innerHTML = markup;
q("#btnTest").addEventListener("click", btnTestClick);
function btnTestClick() {
// Clear log
q("#log").innerHTML = "Testing...";
// Show running
q("#btnTest").disabled = true;
// Run after a pause while the browser updates display
setTimeout(btnTestClickPart2, 0);
}
function btnTestClickPart2() {
try {
runTests();
} catch (e) {
log(`Exception: ${e.message}`);
}
// Re-enable the button
q("#btnTest").disabled = false;
}
function getNumField(name) {
const val = q("#" + name).value.trim();
const num = /^\d+$/.test(val) ? parseInt(val) : NaN;
if (isNaN(num) || num <= 0) {
throw new Error(`Invalid ${name} value ${JSON.stringify(val)}`);
}
return num;
}
function runTests() {
try {
// Clear log
q("#log").innerHTML = "";
const runCount = getNumField("loops");
const length = getNumField("length");
// Do it (we run runCount + 1 times, first time is a warm up)
for (let counter = 0; counter <= runCount; ++counter) {
for (const test of tests) {
if (q("#chk_" + test.name).checked) {
const start = Date.now();
const a = test.func(length);
const time = Date.now() - start;
if (counter == 0) {
// Don't count (warm up), but do check the algorithm works
const invalid = validateResult(a, length);
if (invalid) {
log(`<span class=error>FAILURE</span> with test ${test.name}: ${invalid}`);
return;
}
} else {
// Count this one
log(`#${counter}: ${test.desc}: ${time}ms`);
test.total += time;
}
}
}
}
for (const test of tests) {
if (q("#chk_" + test.name).checked) {
test.avg = test.total / runCount;
if (typeof lowest != "number" || lowest > test.avg) {
lowest = test.avg;
}
}
}
let results =
"<p>Results:" +
"<br>Length: " + length +
"<br>Loops: " + runCount +
"</p>";
for (const test of tests) {
if (q("#chk_" + test.name).checked) {
results +=
`<p ${lowest == test.avg ? " class=winner" : ""}>${test.desc}, average time: ${test.avg}ms</p>`;
}
}
results += "<hr>";
q("#log").insertAdjacentHTML("afterbegin", results);
} catch (e) {
log(e.message);
return;
}
}
function validateResult(a, length) {
if (a.length != length) {
return "Length is wrong";
}
for (let n = length - 1; n >= 0; --n) {
if (a[n] != 0) {
return "Index " + n + " is not zero";
}
}
return undefined;
}
function makeWithCountDownPre(len) {
const a = new Array(len);
while (--len >= 0) {
a[len] = 0;
}
return a;
}
function makeWithCountDownPost(len) {
const a = new Array(len);
while (len-- > 0) {
a[len] = 0;
}
return a;
}
function makeWithCountUp(len) {
const a = new Array(len);
for (let i = 0; i < len; ++i) {
a[i] = 0;
}
return a;
}
function makeWithCountDownArrayUp(len) {
const a = new Array(len);
let i = 0;
while (--len >= 0) {
a[i++] = 0;
}
return a;
}
function makeWithConcat(len) {
if (len == 0) {
return [];
}
let a = [0];
let currlen = 1;
while (currlen < len) {
const rem = len - currlen;
if (rem < currlen) {
a = a.concat(a.slice(0, rem));
} else {
a = a.concat(a);
}
currlen = a.length;
}
return a;
}
function log(msg) {
const p = document.createElement("p");
p.textContent = msg;
q("#log").appendChild(p);
}
body {
font-family: sans-serif;
}
#log p {
margin: 0;
padding: 0;
}
.error {
color: red;
}
.winner {
color: green;
}
<div>
<label for='txtLength'>Length:</label><input type='text' id='length' value='1000'>
<br><label for='txtLoops'>Loops:</label><input type='text' id='loops' value='100000'>
<div id='checkboxes'></div>
<br><input type='button' id='btnTest' value='Test'>
<hr>
<div id='log'></div>
</div>
If you use ES6, you can use Array.from() like this:
Array.from({ length: 3 }, () => 0);
//[0, 0, 0]
Has the same result as
Array.from({ length: 3 }).map(() => 0)
//[0, 0, 0]
Because
Array.from({ length: 3 })
//[undefined, undefined, undefined]
By default Uint8Array, Uint16Array and Uint32Array classes keep zeros as its values, so you don't need any complex filling techniques, just do:
var ary = new Uint8Array(10);
all elements of array ary will be zeros by default.
function makeArrayOf(value, length) {
var arr = [], i = length;
while (i--) {
arr[i] = value;
}
return arr;
}
makeArrayOf(0, 5); // [0, 0, 0, 0, 0]
makeArrayOf('x', 3); // ['x', 'x', 'x']
Note that while is usually more efficient than for-in, forEach, etc.
using object notation
var x = [];
zero filled? like...
var x = [0,0,0,0,0,0];
filled with 'undefined'...
var x = new Array(7);
obj notation with zeros
var x = [];
for (var i = 0; i < 10; i++) x[i] = 0;
As a side note, if you modify Array's prototype, both
var x = new Array();
and
var y = [];
will have those prototype modifications
At any rate, I wouldn't be overly concerned with the efficiency or speed of this operation, there are plenty of other things that you will likely be doing that are far more wasteful and expensive than instanciating an array of arbitrary length containing zeros.
I've tested all combinations of pre-allocating/not pre-allocating, counting up/down, and for/while loops in IE 6/7/8, Firefox 3.5, Chrome, and Opera.
The functions below was consistently the fastest or extremely close in Firefox, Chrome, and IE8, and not much slower than the fastest in Opera and IE 6. It's also the simplest and clearest in my opinion. I've found several browsers where the while loop version is slightly faster, so I'm including it too for reference.
function newFilledArray(length, val) {
var array = [];
for (var i = 0; i < length; i++) {
array[i] = val;
}
return array;
}
or
function newFilledArray(length, val) {
var array = [];
var i = 0;
while (i < length) {
array[i++] = val;
}
return array;
}
ES6 solution:
[...new Array(5)].map(x => 0); // [0, 0, 0, 0, 0]
const arr = Array.from({ length: 10 }).fill(0);
console.log(arr)
If you need to create many zero filled arrays of different lengths during the execution of your code, the fastest way I've found to achieve this is to create a zero array once, using one of the methods mentioned on this topic, of a length which you know will never be exceeded, and then slice that array as necessary.
For example (using the function from the chosen answer above to initialize the array), create a zero filled array of length maxLength, as a variable visible to the code that needs zero arrays:
var zero = newFilledArray(maxLength, 0);
Now slice this array everytime you need a zero filled array of length requiredLength < maxLength:
zero.slice(0, requiredLength);
I was creating zero filled arrays thousands of times during execution of my code, this speeded up the process tremendously.
function zeroFilledArray(size) {
return new Array(size + 1).join('0').split('');
}
Using lodash or underscore
_.range(0, length - 1, 0);
Or if you have an array existing and you want an array of the same length
array.map(_.constant(0));
I have nothing against:
Array.apply(null, Array(5)).map(Number.prototype.valueOf,0);
new Array(5+1).join('0').split('').map(parseFloat);
suggested by Zertosh, but in a new ES6 array extensions allow you to do this natively with fill method. Now IE edge, Chrome and FF supports it, but check the compatibility table
new Array(3).fill(0) will give you [0, 0, 0]. You can fill the array with any value like new Array(5).fill('abc') (even objects and other arrays).
On top of that you can modify previous arrays with fill:
arr = [1, 2, 3, 4, 5, 6]
arr.fill(9, 3, 5) # what to fill, start, end
which gives you: [1, 2, 3, 9, 9, 6]
To create an all new Array
new Array(arrayLength).fill(0);
To add some values at the end of an existing Array
[...existingArray, ...new Array(numberOfElementsToAdd).fill(0)]
Example
//**To create an all new Array**
console.log(new Array(5).fill(0));
//**To add some values at the end of an existing Array**
let existingArray = [1,2,3]
console.log([...existingArray, ...new Array(5).fill(0)]);
The way I usually do it (and is amazing fast) is using Uint8Array. For example, creating a zero filled vector of 1M elements:
var zeroFilled = [].slice.apply(new Uint8Array(1000000))
I'm a Linux user and always have worked for me, but once a friend using a Mac had some non-zero elements. I thought his machine was malfunctioning, but still here's the safest way we found to fix it:
var zeroFilled = [].slice.apply(new Uint8Array(new Array(1000000))
Edited
Chrome 25.0.1364.160
Frederik Gottlieb - 6.43
Sam Barnum - 4.83
Eli - 3.68
Joshua 2.91
Mathew Crumley - 2.67
bduran - 2.55
Allen Rice - 2.11
kangax - 0.68
Tj. Crowder - 0.67
zertosh - ERROR
Firefox 20.0
Allen Rice - 1.85
Joshua - 1.82
Mathew Crumley - 1.79
bduran - 1.37
Frederik Gottlieb - 0.67
Sam Barnum - 0.63
Eli - 0.59
kagax - 0.13
Tj. Crowder - 0.13
zertosh - ERROR
Missing the most important test (at least for me): the Node.js one. I suspect it close to Chrome benchmark.
As of ECMAScript2016, there is one clear choice for large arrays.
Since this answer still shows up near the top on google searches, here's an answer for 2017.
Here's a current jsbench with a few dozen popular methods, including many proposed up to now on this question. If you find a better method please add, fork and share.
I want to note that there is no true most efficient way to create an arbitrary length zero filled array. You can optimize for speed, or for clarity and maintainability - either can be considered the more efficient choice depending on the needs of the project.
When optimizing for speed, you want to: create the array using literal syntax; set the length, initialize iterating variable, and iterate through the array using a while loop. Here's an example.
const arr = [];
arr.length = 120000;
let i = 0;
while (i < 120000) {
arr[i] = 0;
i++;
}
Another possible implementation would be:
(arr = []).length = n;
let i = 0;
while (i < n) {
arr[i] = 0;
i++;
}
But I strongly discourage using this second implantation in practice as it's less clear and doesn't allow you to maintain block scoping on your array variable.
These are significantly faster than filling with a for loop, and about 90% faster than the standard method of
const arr = Array(n).fill(0);
But this fill method is still the most efficient choice for smaller arrays due to it's clarity, conciseness and maintainability. The performance difference likely won't kill you unless you're making a lot of arrays with lengths on the order of thousands or more.
A few other important notes. Most style guides recommend you no longer use varwithout a very special reason when using ES6 or later. Use const for variables that won't be redefined and let for variables that will. The MDN and Airbnb's Style Guide are great places to go for more information on best practices. The questions wasn't about syntax, but it's important that people new to JS know about these new standards when searching through these reams of old and new answers.
Didn't see this method in answers, so here it is:
"0".repeat( 200 ).split("").map( parseFloat )
In result you will get zero-valued array of length 200:
[ 0, 0, 0, 0, ... 0 ]
I'm not sure about the performance of this code, but it shouldn't be an issue if you use it for relatively small arrays.
What about new Array(51).join('0').split('')?
let filled = [];
filled.length = 10;
filled.fill(0);
console.log(filled);
This concat version is much faster in my tests on Chrome (2013-03-21). About 200ms for 10,000,000 elements vs 675 for straight init.
function filledArray(len, value) {
if (len <= 0) return [];
var result = [value];
while (result.length < len/2) {
result = result.concat(result);
}
return result.concat(result.slice(0, len-result.length));
}
Bonus: if you want to fill your array with Strings, this is a concise way to do it (not quite as fast as concat though):
function filledArrayString(len, value) {
return new Array(len+1).join(value).split('');
}
I was testing out the great answer by T.J. Crowder, and came up with a recursive merge based on the concat solution that outperforms any in his tests in Chrome (i didn't test other browsers).
function makeRec(len, acc) {
if (acc == null) acc = [];
if (len <= 1) return acc;
var b = makeRec(len >> 1, [0]);
b = b.concat(b);
if (len & 1) b = b.concat([0]);
return b;
},
call the method with makeRec(29).
It might be worth pointing out, that Array.prototype.fill had been added as part of the ECMAScript 6 (Harmony) proposal. I would rather go with the polyfill written below, before considering other options mentioned on the thread.
if (!Array.prototype.fill) {
Array.prototype.fill = function(value) {
// Steps 1-2.
if (this == null) {
throw new TypeError('this is null or not defined');
}
var O = Object(this);
// Steps 3-5.
var len = O.length >>> 0;
// Steps 6-7.
var start = arguments[1];
var relativeStart = start >> 0;
// Step 8.
var k = relativeStart < 0 ?
Math.max(len + relativeStart, 0) :
Math.min(relativeStart, len);
// Steps 9-10.
var end = arguments[2];
var relativeEnd = end === undefined ?
len : end >> 0;
// Step 11.
var final = relativeEnd < 0 ?
Math.max(len + relativeEnd, 0) :
Math.min(relativeEnd, len);
// Step 12.
while (k < final) {
O[k] = value;
k++;
}
// Step 13.
return O;
};
}
Shortest for loop code
a=i=[];for(;i<100;)a[i++]=0;
edit:
for(a=i=[];i<100;)a[i++]=0;
or
for(a=[],i=100;i--;)a[i]=0;
Safe var version
var a=[],i=0;for(;i<100;)a[i++]=0;
edit:
for(var i=100,a=[];i--;)a[i]=0;
My fastest function would be:
function newFilledArray(len, val) {
var a = [];
while(len--){
a.push(val);
}
return a;
}
var st = (new Date()).getTime();
newFilledArray(1000000, 0)
console.log((new Date()).getTime() - st); // returned 63, 65, 62 milliseconds
Using the native push and shift to add items to the array is much faster (about 10 times) than declaring the array scope and referencing each item to set it's value.
fyi: I consistently get faster times with the first loop, which is counting down, when running this in firebug (firefox extension).
var a = [];
var len = 1000000;
var st = (new Date()).getTime();
while(len){
a.push(0);
len -= 1;
}
console.log((new Date()).getTime() - st); // returned 863, 894, 875 milliseconds
st = (new Date()).getTime();
len = 1000000;
a = [];
for(var i = 0; i < len; i++){
a.push(0);
}
console.log((new Date()).getTime() - st); // returned 1155, 1179, 1163 milliseconds
I'm interested to know what T.J. Crowder makes of that ? :-)
I knew I had this proto'd somewhere :)
Array.prototype.init = function(x,n)
{
if(typeof(n)=='undefined') { n = this.length; }
while (n--) { this[n] = x; }
return this;
}
var a = (new Array(5)).init(0);
var b = [].init(0,4);
Edit: tests
In response to Joshua and others methods I ran my own benchmarking, and I'm seeing completely different results to those reported.
Here's what I tested:
//my original method
Array.prototype.init = function(x,n)
{
if(typeof(n)=='undefined') { n = this.length; }
while (n--) { this[n] = x; }
return this;
}
//now using push which I had previously thought to be slower than direct assignment
Array.prototype.init2 = function(x,n)
{
if(typeof(n)=='undefined') { n = this.length; }
while (n--) { this.push(x); }
return this;
}
//joshua's method
function newFilledArray(len, val) {
var a = [];
while(len--){
a.push(val);
}
return a;
}
//test m1 and m2 with short arrays many times 10K * 10
var a = new Date();
for(var i=0; i<10000; i++)
{
var t1 = [].init(0,10);
}
var A = new Date();
var b = new Date();
for(var i=0; i<10000; i++)
{
var t2 = [].init2(0,10);
}
var B = new Date();
//test m1 and m2 with long array created once 100K
var c = new Date();
var t3 = [].init(0,100000);
var C = new Date();
var d = new Date();
var t4 = [].init2(0,100000);
var D = new Date();
//test m3 with short array many times 10K * 10
var e = new Date();
for(var i=0; i<10000; i++)
{
var t5 = newFilledArray(10,0);
}
var E = new Date();
//test m3 with long array created once 100K
var f = new Date();
var t6 = newFilledArray(100000, 0)
var F = new Date();
Results:
IE7 deltas:
dA=156
dB=359
dC=125
dD=375
dE=468
dF=412
FF3.5 deltas:
dA=6
dB=13
dC=63
dD=8
dE=12
dF=8
So by my reckoning push is indeed slower generally but performs better with longer arrays in FF but worse in IE which just sucks in general (quel surprise).
Anonymous function:
(function(n) { while(n-- && this.push(0)); return this; }).call([], 5);
// => [0, 0, 0, 0, 0]
A bit shorter with for-loop:
(function(n) { for(;n--;this.push(0)); return this; }).call([], 5);
// => [0, 0, 0, 0, 0]
Works with any Object, just change what's inside this.push().
You can even save the function:
function fill(size, content) {
for(;size--;this.push(content));
return this;
}
Call it using:
var helloArray = fill.call([], 5, 'hello');
// => ['hello', 'hello', 'hello', 'hello', 'hello']
Adding elements to an already existing array:
var helloWorldArray = fill.call(helloArray, 5, 'world');
// => ['hello', 'hello', 'hello', 'hello', 'hello', 'world', 'world', 'world', 'world', 'world']
Performance: http://jsperf.com/zero-filled-array-creation/25

Categories