One option is to use reduce:
let arrs = [[1, 2], [3, 4], [5, 6]];
arrs.reduce((a, b) => [...a, ...b], []);
Of course, this is a slow solution (quadratic time). Alternatively, if you can use Lodash, _.flatten does exactly what you want, and does it more efficiently (linear time).
EDIT
Or, adapted from Xotic750's comment below,
[].concat(...arrs);
Which should be efficient (linear time).
Answer from Brian McCutchon on Stack OverflowOne option is to use reduce:
let arrs = [[1, 2], [3, 4], [5, 6]];
arrs.reduce((a, b) => [...a, ...b], []);
Of course, this is a slow solution (quadratic time). Alternatively, if you can use Lodash, _.flatten does exactly what you want, and does it more efficiently (linear time).
EDIT
Or, adapted from Xotic750's comment below,
[].concat(...arrs);
Which should be efficient (linear time).
Another option could be:
const nArrays = [
[1, 2, 3, 4, 5],
[6, 7, 8, 9],
[10, 11]
];
const flattened = [].concat(...nArrays);
console.log(flattened)
Videos
lodash merge actually does what I exactly need.
https://blog.mariusschulz.com/2015/03/30/combining-settings-objects-with-lodash-assign-or-merge
// Defined within your component
var defaultSettings = {
strictMode: true,
formatting: {
finalNewline: true,
quotes: "double"
}
};
// Provided by the developer using your component
var userSettings = {
formatting: {
quotes: "single"
}
};
var assignedSettings = _.assign({}, defaultSettings, userSettings);
// {
// strictMode: true,
// formatting: {
// quotes: "single"
// }
// }
var mergedSettings = _.merge({}, defaultSettings, userSettings);
// {
// strictMode: true,
// formatting: {
// finalNewline: true,
// quotes: "single"
// }
// }
The spread operator is for arrays and iterable objects.
If x = ['A','B','C'];
Then ...x is equals to writing x[0], x[1], x[2].
You want to merge the nested objects key by key from each object, not replace the previous values.
let key;
let c = {};
for (key in a) {
c[key] = Object.assign({}, a[key]);
}
for (key in b) {
c[key] = Object.assign(c[key] || {}, b[key]);
}
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');
.concat() is faster, otherwise they work the same assuming both arguments are lists. Here's the time it takes to merge two arrays with 10 million elements each (lower is better):
| Browser | [...a, ...b] |
a.concat(b) |
|---|---|---|
| Chrome 113 | 350ms | 30ms |
| Firefox 113 | 400ms | 63ms |
| Safari 16.4 | 92ms | 71ms |
I ran this code on an M1 MacBook Air with 8GB of RAM:
const arraySize = 10000000;
const trials = 50;
const array1 = [];
const array2 = [];
for (let i = 0; i < arraySize; ++i) {
array1.push(i);
array2.push(i);
}
let spreadTime = 0;
for (let i = 0; i < trials; ++i) {
const start = performance.now();
const array3 = [...array1, ...array2];
const end = performance.now();
spreadTime += end - start;
}
let concatTime = 0;
for (let i = 0; i < trials; ++i) {
const start = performance.now();
const array3 = array1.concat(array2);
const end = performance.now();
concatTime += end - start;
}
// performance.now() returns milliseconds with a
// 5 microsecond resolution in isolated contexts and a
// 100 microsecond resolution in non-isolated contexts.
spreadTime = Math.round(spreadTime / trials * 1000) / 1000;
concatTime = Math.round(concatTime / trials * 1000) / 1000;
console.log(`${arraySize} items - spread: ${spreadTime}ms concat: ${concatTime}ms`);