In modern JavaScript, there are often several ways to achieve the same result. A common LeetCode problem, Concatenation of Array, asks us to take an array nums and return a new array that is twice as long, consisting of nums followed by nums.
While the task is simple, the way we solve it reveals a lot about our priorities as developers: Readability vs. Performance.
The Problem
Input: nums = [1, 2, 1]
Output: [1, 2, 1, 1, 2, 1]
Level 1: The Modern One-Liner (Spread Operator)
If you’re working on a standard React app or a small utility function, readability is king. The spread operator (…) is the most idiomatic way to handle this in ES6+.
function getConcatenation(nums) {
return [...nums, ...nums];
};
Why use it?
- Declarative: It describes what you want to do, not how to do it.
- Concise: A single line of code that is instantly understandable by any modern JS developer.
Level 2: The Built-in Specialist (concat)
Before the spread operator, we had Array.prototype.concat. While it looks similar to the spread approach, it is a specialized method designed specifically for joining arrays.
function getConcatenation(nums) {
return nums.concat(nums);
};
Why use it?
- Performance: In many JavaScript engines (like V8), concat is highly optimized at the C++ level. It often outperforms the spread operator because it doesn’t have to deal with the overhead of the “iterator protocol” that the spread operator uses.
Level 3: The High-Performance Approach (Manual Pre-allocation)
When you are dealing with massive datasets or competing for the top spot on the LeetCode leaderboard, you need to minimize memory re-allocation.
In the previous two methods, the JavaScript engine starts with a small array and “grows” it as it adds elements. Every time the array grows past its capacity, the engine has to find a new, larger spot in memory and copy everything over.
By pre-allocating the array size, we avoid this overhead entirely.
function getConcatenation(nums) {
const n = nums.length;
// We tell the engine EXACTLY how much memory we need upfront
const ans = new Array(2 * n);
for (let i = 0; i < n; i++) {
ans[i] = nums[i]; // Assign to the first half
ans[i + n] = nums[i]; // Assign to the second half
}
return ans;
};
Why use it?
- Speed: This is the fastest method because it uses direct index assignment.
- Memory Efficiency: It prevents “Garbage Collection” spikes caused by creating intermediate temporary arrays during the growth phase.
Summary: Which one should you choose?
| Method | Complexity | Use Case |
| Spread […] | $O(n)$ | Everyday web development & small arrays. |
| concat() | $O(n)$ | Clean code that needs a bit more speed. |
| Pre-allocation | $O(n)$ | High-performance apps, Data Science, or LeetCode. |