What is the more efficient way to insert an array inside another array.
a1 = [1,2,3,4,5];
a2 = [21,22];
newArray - a1.insertAt(2,a2) -> [1,2, 21,22, 3,4,5];
Iterating a2 using splice looks a bit awfull from a performance point of view if a2 array is large.
Thanks.
-
1have a look at stackoverflow.com/questions/586182/…Endophage– Endophage2011年08月11日 20:45:05 +00:00Commented Aug 11, 2011 at 20:45
-
2it's not one item but an array, so splice is not workingic3– ic32011年08月11日 20:51:16 +00:00Commented Aug 11, 2011 at 20:51
11 Answers 11
You can use splice
combined with some apply
trickery:
a1 = [1,2,3,4,5];
a2 = [21,22];
a1.splice.apply(a1, [2, 0].concat(a2));
console.log(a1); // [1, 2, 21, 22, 3, 4, 5];
In ES2015+, you could use the spread operator instead to make this a bit nicer
a1.splice(2, 0, ...a2);
-
11@icCube: I just ran a benchmark, comparing this method to that in patrick's answer. It starts with a1 and a2 as in the example, and injects the a2 variable into a1 10,000 times (so that in the end, you have an array with 20,005 elements). This method took: 81ms, the slice + concat method took 156ms (tested on Chrome 13). Of course, this comes with the standard caveat that in most cases, the legibility will be more important than the speed.nickf– nickf2011年08月14日 17:37:55 +00:00Commented Aug 14, 2011 at 17:37
-
3@nick: In chrome if the arrays hold over 130000 items
apply
will throw a stackoverflow exception. jsfiddle.net/DcHCY Also happens in jsPerf I believe in FF and IE the threshhold is around 500kNope– Nope2013年12月02日 14:22:36 +00:00Commented Dec 2, 2013 at 14:22 -
why do you use apply as opposed to calling splice directly and passing the arguments?Peter P.– Peter P.2014年06月11日 21:22:47 +00:00Commented Jun 11, 2014 at 21:22
-
@FrançoisWahl, this is a serious issue that folks often ignore in their answer. I've took your example and made it work with north of 1M elements: stackoverflow.com/a/41466395/1038326Gabriel Kohen– Gabriel Kohen2017年01月04日 14:37:59 +00:00Commented Jan 4, 2017 at 14:37
-
2I have just tried this (June 2017) and the fastest way for both small array sizes and large array sizes (on Chrome, FF and Edge) appears to be
target.slice(0, insertIndex).concat(insertArr, target.slice(insertIndex));
jsperf.com/inserting-an-array-within-an-arrayAlex Dima– Alex Dima2017年06月15日 09:45:39 +00:00Commented Jun 15, 2017 at 9:45
You can now do this if using ES2015 or later:
var a1 = [1,2,3,4,5];
var a2 = [21,22];
a1.splice(2, 0, ...a2);
console.log(a1) // => [1,2,21,22,3,4,5]
Refer to this for documenation on the spread (...) operator https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator
Had it wrong at first. Should have used concat()
instead.
var a1 = [1,2,3,4,5],
a2 = [21,22],
startIndex = 0,
insertionIndex = 2,
result;
result = a1.slice(startIndex, insertionIndex).concat(a2).concat(a1.slice(insertionIndex));
Example: http://jsfiddle.net/f3cae/1/
This expression uses slice(0, 2)
[docs] to return the first two elements of a1
(where 0
is the starting index, and 2
is the element deleteCount, though a1
is not altered).
Intermediate result: [1,2]
It then uses concat(a2)
[docs] to append a2
to the end of the [1,2]
.
Intermediate result:[1,2,21,22]
.
Next, a1.slice(2)
is called within a trailing .concat()
at the tail end of this expression, which amounts to [1,2,21,22].concat(a1.slice(2))
.
A call to slice(2)
, having a positive integer argument, will return all elements after the 2nd element, counting by natural numbers (as in, there are five elements, so [3,4,5]
will be returned from a1
). Another way to say this is that the singular integer index argument tells a1.slice()
at which position in the array to start returning elements from (index 2 is the third element).
Intermediate result: [1,2,21,22].concat([3,4,5])
Finally, the second .concat()
adds [3,4,5]
to the the end of [1,2,21,22]
.
Result: [1,2,21,22,3,4,5]
It may be tempting to alter Array.prototype
, but one can simply extend the Array object using prototypal inheritance and inject said new object into your projects.
However, for those living on the edge ...
Example: http://jsfiddle.net/f3cae/2/
Array.prototype.injectArray = function( idx, arr ) {
return this.slice( 0, idx ).concat( arr ).concat( this.slice( idx ) );
};
var a1 = [1,2,3,4,5];
var a2 = [21,22];
var result = a1.injectArray( 2, a2 );
-
I get a1 with 6 items not 7 -> [1,2,[21,22],3,4,5]ic3– ic32011年08月11日 20:47:04 +00:00Commented Aug 11, 2011 at 20:47
-
works ! thanks a lot, let's wait some days to but this as the best answer... weird they don't have a js function for this.ic3– ic32011年08月11日 21:01:19 +00:00Commented Aug 11, 2011 at 21:01
-
In sum, the JS engine must return four arrays before the expression is finished. I like the logic of the solution, but it may not be optimal in terms of space considerations. However I agree that it is nifty and very cross browser compatible. There is a cost to using the spread operator as it works on collections, but I might take that over returning four arrays in the long run.Anthony Rutledge– Anthony Rutledge2019年04月17日 21:37:52 +00:00Commented Apr 17, 2019 at 21:37
The spread operator allows an expression to be expanded in places where multiple arguments (for function calls) or multiple elements (for array literals) are expected.
a2 = [21,22];
a1 = [1,2,...a2,3,4,5];//...a2 is use of spread operator
console.log(a1);
There are some truly creative answers to this question here. Here is a simple solution for those just starting out with arrays. It can be made to work all the way down to ECMAScript 3 compliant browsers, if desired.
Know something about splice before getting started.
Mozilla Developer Network: Array.prototype.splice()
First, understand two important forms of .splice()
.
let a1 = [1,2,3,4],
a2 = [1,2];
Method 1) Remove x (deleteCount) elements, starting from a desired index.
let startIndex = 0,
deleteCount = 2;
a1.splice(startIndex, deleteCount); // returns [1,2], a1 would be [3,4]
Method 2) Remove elements after a desired start index to the end of the array.
a1.splice(2); // returns [3,4], a1 would be [1,2]
Using .splice()
, a goal could be to split a1
into head and tail arrays by using one of the two forms above.
Using method #1, the return value would become the head, and a1
the tail.
let head = a1.splice(startIndex, deleteCount); // returns [1,2], a1 would be [3,4]
Now, in one fell swoop, concatenate the head, body (a2
), and tail
[].concat(head, a2, a1);
Thus, this solution is more like the real world than any other presented thus far. Is this not what you would do with Legos? ;-) Here is a function, done using method #2.
/**
*@param target Array The array to be split up into a head and tail.
*@param body Array The array to be inserted between the head and tail.
*@param startIndex Integer Where to split the target array.
*/
function insertArray(target, body, startIndex)
{
let tail = target.splice(startIndex); // target is now [1,2] and the head
return [].concat(target, body, tail);
}
let newArray = insertArray([1, 2, 3, 4], ["a", "b"], 2); // [1, 2, "a", "b", 3, 4]
Shorter:
/**
*@param target Array The array to be split up into a head and tail.
*@param body Array The array to be inserted between the head and tail.
*@param startIndex Integer Where to split the target array.
*/
function insertArray(target, body, startIndex)
{
return [].concat(target, body, target.splice(startIndex));
}
Safer:
/**
*@param target Array The array to be split up into a head and tail.
*@param body Array The array to be inserted between the head and tail.
*@param startIndex Integer Where to split the target array.
*@throws Error The value for startIndex must fall between the first and last index, exclusive.
*/
function insertArray(target, body, startIndex)
{
const ARRAY_START = 0,
ARRAY_END = target.length - 1,
ARRAY_NEG_END = -1,
START_INDEX_MAGNITUDE = Math.abs(startIndex);
if (startIndex === ARRAY_START) {
throw new Error("The value for startIndex cannot be zero (0).");
}
if (startIndex === ARRAY_END || startIndex === ARRAY_NEG_END) {
throw new Error("The startIndex cannot be equal to the last index in target, or -1.");
}
if (START_INDEX_MAGNITUDE >= ARRAY_END) {
throw new Error("The absolute value of startIndex must be less than the last index.");
}
return [].concat(target, body, target.splice(startIndex));
}
The advantages of this solution include:
1) A simple premise dominates the solution--fill an empty array.
2) Head, body, and tail nomenclature feels natural.
3) No double call to .slice()
. No slicing at all.
4) No .apply()
. Highly unnecessary.
5) Method chaining is avoided.
6) Works in ECMAScript 3 and 5 simply by using var
instead of let
or const
.
**7) Ensures that there will be a head and tail to slap on to the body, unlike many other solutions presented. If you are adding an array before, or after, the bounds, you should at least be using .concat()
!!!!
Note: Use of the spread opearator ...
makes all of this much easier to accomplish.
I wanted to find a way to do this with splice()
and no iterating: http://jsfiddle.net/jfriend00/W9n27/.
a1 = [1,2,3,4,5];
a2 = [21,22];
a2.unshift(2, 0); // put first two params to splice onto front of array
a1.splice.apply(a1, a2); // pass array as arguments parameter to splice
console.log(a1); // [1, 2, 21, 22, 3, 4, 5];
In general purpose function form:
function arrayInsertAt(destArray, pos, arrayToInsert) {
var args = [];
args.push(pos); // where to insert
args.push(0); // nothing to remove
args = args.concat(arrayToInsert); // add on array to insert
destArray.splice.apply(destArray, args); // splice it in
}
-
This modifies the
a2
array as well, which is probably quite undesirable. Also, you don't need to go to theArray.prototype
when you have the slice function right there ona1
ora2
.nickf– nickf2011年08月11日 21:09:10 +00:00Commented Aug 11, 2011 at 21:09 -
I was aware it was modifying a2. OP can decide if that's a problem or not. Is there anything wrong with going to the prototype to get the method? It seemed more appropriate to me since I just wanted the method and the object was being added in the
apply()
method.jfriend00– jfriend002011年08月11日 21:13:35 +00:00Commented Aug 11, 2011 at 21:13 -
Well no, it's the exact same function, but one is shorter:
Array.prototype.splice
vsa1.splice
:)nickf– nickf2011年08月11日 21:39:07 +00:00Commented Aug 11, 2011 at 21:39 -
Added general purpose array insertion function.jfriend00– jfriend002011年08月11日 21:56:38 +00:00Commented Aug 11, 2011 at 21:56
-
.concat
returns a new array, it doesn't modify the existing one likesplice
. Should beargs = args.concat(arrayToInsert);
nickf– nickf2011年08月11日 22:05:08 +00:00Commented Aug 11, 2011 at 22:05
var a1 = [1,2,3,4,5];
var a2 = [21,22];
function injectAt(d, a1, a2) {
for(var i=a1.length-1; i>=d; i--) {
a1[i + a2.length] = a1[i];
}
for(var i=0; i<a2.length; i++) {
a1[i+d] = a2[i];
}
}
injectAt(2, a1, a2);
alert(a1);
As mentioned in another thread,the answers above will not work in very large arrays (200K elements). See alternate answer here involving splice and manual push: https://stackoverflow.com/a/41465578/1038326
Array.prototype.spliceArray = function(index, insertedArray) {
var postArray = this.splice(index);
inPlacePush(this, insertedArray);
inPlacePush(this, postArray);
function inPlacePush(targetArray, pushedArray) {
// Not using forEach for browser compatability
var pushedArrayLength = pushedArray.length;
for (var index = 0; index < pushedArrayLength; index++) {
targetArray.push(pushedArray[index]);
}
}
}
-
In Visual Code the limit is 120K items, I posted an additional answer to explain this issueThangXD Software– ThangXD Software2022年09月24日 07:52:48 +00:00Commented Sep 24, 2022 at 7:52
Here's my version with no special tricks:
function insert_array(original_array, new_values, insert_index) {
for (var i=0; i<new_values.length; i++) {
original_array.splice((insert_index + i), 0, new_values[i]);
}
return original_array;
}
-
In this case, it may be simpler to just .reverse() the new_values array, instead of incrementing insert_index for each iteration. Of course, when start_index is zero or start_index - 1, the efficiency of this solution is poor, compare to just using .concat() without any explicit loops, or unshift() or push()` with
.apply()
.Anthony Rutledge– Anthony Rutledge2019年04月19日 13:19:30 +00:00Commented Apr 19, 2019 at 13:19
Be careful with spread operator, for example you need insert a huge number of items into an array as below
let C = [];
for(let i=0;i<200000;i++){
C.push(i);
}
let A = [1,2,3,4,5];
A.splice(2,0,...C); //this will cause RangeError: Maximum call stack size exceeded
Because, spread operator splits items in an array into spreading variables and past them into splice method.
The spread operator has a limit of N items. (in Visual Code, I realized N is around 120000)
The pragmatical way to ensure no error met (I did in my real project with over 200000 items)
const N = 120000;
function insertArray(origin,index,inserted){
if(inserted.length>N){
if(index>0 && index<origin.length-1){
const chunk1 = origin.slice(0,index);
const chunk2 = origin.slice(index);
return [].concat(chunk1,inserted,chunk2);
}else{
return null;
}
}else{
return origin.splice(index,0,...inserted);
}
}
If you want to insert another array into an array without creating a new one, the easiest way is to use either push
or unshift
with apply
Eg:
a1 = [1,2,3,4,5];
a2 = [21,22];
// Insert a1 at beginning of a2
a2.unshift.apply(a2,a1);
// Insert a1 at end of a2
a2.push.apply(a2,a1);
This works because both push
and unshift
take a variable number of arguments.
A bonus, you can easily choose which end to attach the array from!
-
Just by doing
a1.concat(a2)
ora2.concat(a1)
I have devised something easier than what you suggest. However, the issue is inserting an array between array bounds, not exclusively adding an array to the beginning or end. ;-)Anthony Rutledge– Anthony Rutledge2019年04月19日 13:08:46 +00:00Commented Apr 19, 2019 at 13:08