73

Is there a better way than this to splice an array into another array in javascript

var string = 'theArray.splice('+start+', '+number+',"'+newItemsArray.join('","')+'");';
eval(string);
scottt
  • 7,008
  • 27
  • 37
wheresrhys
  • 22,558
  • 19
  • 94
  • 162
  • As a general rule of thumb, one should avoid `eval` at all costs in JavaScript. Using `eval` is **always** (without exception) more buggy, more error-prone, harder to debug, and much slower than the alternative. If you are tempted to use `eval`, then seek help or rewrite your code from scratch. `eval` is the bane of JavaScript. I have never once used `eval`. If I need to execute code, I use the much superior `Function`. In this case, your code might fail if `start`is a string, if `number` is a string, or if any of the strings in `newItemsArray` contain quotes or line-breaks or carriage-returns – Jack G Jan 19 '21 at 02:11

8 Answers8

142

You can use apply to avoid eval:

var args = [start, number].concat(newItemsArray);
Array.prototype.splice.apply(theArray, args);

The apply function is used to call another function, with a given context and arguments, provided as an array, for example:

If we call:

var nums = [1,2,3,4];
Math.min.apply(Math, nums);

The apply function will execute:

Math.min(1,2,3,4);
Christian C. Salvadó
  • 807,428
  • 183
  • 922
  • 838
  • 4
    Although, it turns out I need to do Array.prototype.splice.apply(theArray, [start, number].concat(newItemsArray)) as all the arguments, not just some, must be in one array. – wheresrhys Aug 28 '09 at 16:54
  • 3
    +1 for the `[start, number].concat(array)` pattern. that is nifty – Claudiu Oct 03 '13 at 17:36
  • 1
    Has anyone tried to enter a large amount of element under Chrome? Around 130K elements we seem to get Stackoverflow exception. See: http://jsfiddle.net/DcHCY/ – Gabriel Kohen Jan 04 '17 at 12:53
  • 2
    @Claudiu another nifty trick is to replace ```Array.prototype``` with ```[]```. So the command can be: ```[].splice.apply(theArray, [start, removeCount].concat(newItemsArray);``` – snapfractalpop Apr 09 '17 at 10:10
  • Very late resposne to Gabriel Kohen, but using apply, or the ... operator both have problems with large data sets, you are gated by the argument limit of your specific javascript engine. – CSJordan Jun 25 '19 at 16:42
  • Like a c# [params keyword](https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/params), `splice` allows [an open ended number of params](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice). But if you pass `splice` an array directly: `a.splice(start, number, newItems)` -- it'd insert `newItems` as a _single element_. With the `args` array, [apply](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply) (and **NOT** `call`) "spreads" our `newItems` + args array into params in es5-. Clever. – ruffin Mar 23 '22 at 17:01
77

UPDATE: ES6 version

If you're coding in ES6, you can use the "spread operator" (...).

array.splice(index, 0, ...arrayToInsert);

To learn more about the spread operator see the MDN documentation.


The 'old' ES5 way

If you wrap the top answer into a function you get this:

function insertArrayAt(array, index, arrayToInsert) {
    Array.prototype.splice.apply(array, [index, 0].concat(arrayToInsert));
}

You would use it like this:

var arr = ["A", "B", "C"];
insertArrayAt(arr, 1, ["x", "y", "z"]);
alert(JSON.stringify(arr)); // output: A, x, y, z, B, C

You can check it out in this jsFiddle: http://jsfiddle.net/luisperezphd/Wc8aS/

ruffin
  • 16,507
  • 9
  • 88
  • 138
Luis Perez
  • 27,650
  • 10
  • 79
  • 80
14

This question is really old, but with ES6, there's a simpler way to do this using the spread operator:

sourceArray.splice(index, 0, ...insertedArray)

If you're using uncompiled javascript in the browser, be sure to check if it's supported in your target browser at https://kangax.github.io/compat-table/es6/#test-spread_(...)_operator.


Also, this may be slightly off topic, but if you don't want or need to modify the original array, but could use a new array instead, consider this approach:

mergedArray = sourceArray.slice(0, index).concat(insertedArray, sourceArray.slice(index))
undefined
  • 6,208
  • 3
  • 49
  • 59
6

You can also add such a function to the Array prototype, if you want something that is almost identical to the splice method. E.g.

Array.prototype.spliceArray = function(index, n, array) {
    return Array.prototype.splice.apply(this, [index, n].concat(array));
}

Then usage would simply be:

var array = ["A","B","C","","E","F"];

array.splice(3,1,"D");
// array is ["A","B","C","D","E","F"]

array.spliceArray(3,3,["1","2","3"]);
// array is ["A","B","C","1","2","3"]

See it in action here: http://jsfiddle.net/TheMadDeveloper/knv2f8bb/1/

Some notes:

  • The splice function modifies the array directly, but returns the an array of elements that were removed... not the spliced array.
  • While it's normally not recommended to extend core javascript classes, this is relatively benign with most standard frameworks.
  • Extending Array won't work in cases where specialized array classes are used, such as an ImageData data Uint8ClampedArray.
TheMadDeveloper
  • 1,587
  • 15
  • 28
1

The answers above that involve splice.apply and insert the array in a one liner will blow up the stack in a stack overflow for large array. See example here: http://jsfiddle.net/gkohen/u49ku99q/ You might have to slice and and push each item of the inserted and remaining part of the original array for it to work. See fiddle: http://jsfiddle.net/gkohen/g9abppgy/26/

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]);
       }
   }
}
Gabriel Kohen
  • 4,166
  • 4
  • 31
  • 46
  • 1
    Do you know this problem occurs for splice.apply, or if it's browser specific? – Drenai Nov 25 '18 at 08:36
  • 1
    @Drenai, this is a per browser limitation which you can easily hit with large arrays as mentioned [here](https://stackoverflow.com/questions/22747068/is-there-a-max-number-of-arguments-javascript-functions-can-accept) – Gabriel Kohen Dec 12 '18 at 19:31
1

There are a lot of clever answers here, but the reason you use splice is so that it puts the elements into the current array without creating another. If you have to create an array to concat() against so you can use apply() then you're creating 2 additional trash arrays! Sorta defeats the whole purpose of writing esoteric Javascript. Besides if you don't care about that memory usage stuff (and you should) just dest = src1.concat(src2); it is infinitely more readable. So here's is my smallest number of lines while staying efficient answer.

for( let item of src ) dest.push( item );

Or if you'd like to polyfill it and have a little better browser support back:

src.forEach( function( x ) { dest.push(x); });

I'm sure the first is more performant (it's a word ;), but not supported in all browsers out there in the wild.

chubbsondubs
  • 37,646
  • 24
  • 106
  • 138
0

If you don't want to concatenate inserting items to first two parameters of Array.splice(), an elegant way is to use Function.bind() and Function.apply() together.

theArray.splice.bind(null, startIndex, deleteCount).apply(newItemsArray);
Sen Jacob
  • 3,384
  • 3
  • 35
  • 61
-1

I wanted to have a function which would take only part of the source array so I have mine slightly different based off CMS's answer

function spliceArray(array, index, howmany, source, start, end) {
    var arguments;
  if( source !== undefined ){
    arguments = source.slice(start, end);
    arguments.splice(0,0, index, howmany);
  } else{
   arguments = [index, howmany];
  }
    return Array.prototype.splice.apply(array, arguments)
}

Array.prototype.spliceArray = function(index, howmany, source, start, end) {
    return spliceArray(this, index, howmany, source, start, end);
}

You can see it at: https://jsfiddle.net/matthewvukomanovic/nx858uz5/

Community
  • 1
  • 1
Matt Vukomanovic
  • 1,392
  • 1
  • 15
  • 23