Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

spread operator vs array.concat()

What is the difference between spread operator and array.concat()

let parts = ['four', 'five']; let numbers = ['one', 'two', 'three']; console.log([...numbers, ...parts]);

Array.concat() function

let parts = ['four', 'five']; let numbers = ['one', 'two', 'three']; console.log(numbers.concat(parts));

Both results are same. So, what kind of scenarios we want to use them? And which one is best for performance?

like image 643
Ramesh Rajendran Avatar asked Feb 19 '18 11:02

Ramesh Rajendran


People also ask

How do you concatenate an array using the spread operator?

You can use either the spread operator [... array1, ... array2] , or a functional way []. concat(array1, array2) to merge 2 or more arrays.

Is concat faster than push?

concat performs at 0.40 ops/sec, while . push performs at 378 ops/sec. push is 945x faster than concat ! This difference might not be linear, but it is already is already significant at this small scale.

Does spread operator work on arrays?

The spread operator unpacks elements of iterable objects such as arrays, sets, and maps into a list. The rest paramter is also denoted by three dots (…). However, it packs the remaining arguments of a function into an array. The spread operator can be used to clone an iterable object or merge iterable objects into one.

What can I use instead of a spread operator?

One of the alternatives to the spread operator is the Object. assign function. Here is the same function using the object. assign function.


1 Answers

concat and spreads are very different when the argument is not an array.

When the argument is not an array, concat adds it as a whole, while ... tries to iterate it and fails if it can't. Consider:

a = [1, 2, 3] x = 'hello';  console.log(a.concat(x));  // [ 1, 2, 3, 'hello' ] console.log([...a, ...x]); // [ 1, 2, 3, 'h', 'e', 'l', 'l', 'o' ] 

Here, concat treats the string atomically, while ... uses its default iterator, char-by-char.

Another example:

x = 99;  console.log(a.concat(x));   // [1, 2, 3, 99] console.log([...a, ...x]);  // TypeError: x is not iterable 

Again, for concat the number is an atom, ... tries to iterate it and fails.

Finally:

function* gen() { yield *'abc' }  console.log(a.concat(gen()));   // [ 1, 2, 3, Object [Generator] {} ] console.log([...a, ...gen()]);  // [ 1, 2, 3, 'a', 'b', 'c' ] 

concat makes no attempt to iterate the generator and appends it as a whole, while ... nicely fetches all values from it.

To sum it up, when your arguments are possibly non-arrays, the choice between concat and ... depends on whether you want them to be iterated.

The above describes the default behaviour of concat, however, ES6 provides a way to override it with Symbol.isConcatSpreadable. By default, this symbol is true for arrays, and false for everything else. Setting it to true tells concat to iterate the argument, just like ... does:

str = 'hello' console.log([1,2,3].concat(str)) // [1,2,3, 'hello']  str = new String('hello'); str[Symbol.isConcatSpreadable] = true; console.log([1,2,3].concat(str)) // [ 1, 2, 3, 'h', 'e', 'l', 'l', 'o' ] 

Performance-wise concat is faster, probably because it can benefit from array-specific optimizations, while ... has to conform to the common iteration protocol. Timings:

let big = (new Array(1e5)).fill(99);  let i, x;    console.time('concat-big');  for(i = 0; i < 1e2; i++) x = [].concat(big)  console.timeEnd('concat-big');    console.time('spread-big');  for(i = 0; i < 1e2; i++) x = [...big]  console.timeEnd('spread-big');      let a = (new Array(1e3)).fill(99);  let b = (new Array(1e3)).fill(99);  let c = (new Array(1e3)).fill(99);  let d = (new Array(1e3)).fill(99);    console.time('concat-many');  for(i = 0; i < 1e2; i++) x = [1,2,3].concat(a, b, c, d)  console.timeEnd('concat-many');    console.time('spread-many');  for(i = 0; i < 1e2; i++) x = [1,2,3, ...a, ...b, ...c, ...d]  console.timeEnd('spread-many');
like image 194
7 revs Avatar answered Nov 10 '22 01:11

7 revs