Is there a function to copy set of elements within a range in an array into a range in another array?

Viewed 1699

I have a place in my javascript code where I need to do an operation like

copying a specified range from one array into specified range in another array.

The operation is similar to

1) System.arraycopy in java ->

System.arraycopy(array1, index1, array2, index3, index4 - index4 + 1);

2) copy in go ->

copy(array2[index3:index4 + 1], array1[index1:index2+1])

3) slice in python ->

array2[index3: index4 + 1] = arr[index1: index2+1]

For now I am iterating it by hand and doing it. But I did not any util function in js to do that. Is there really one ?

Update 1: It should exactly only copy and not add or remove elements from any of the two given arrays.

It should behave like this implementation:

function arraycopy(src, srcPos, dst, dstPos, length) {
    let j = dstPos;
    let tempArr = src.slice(srcPos, srcPos + length);
    for (let e in tempArr) {
        dst[j] =  tempArr[e];
        j++;
    }
};

Update 2: (See some answers below with caution and see if it will fit your use case(like in huge dataset case)) Many answers below used splice in such a way it will break, if the range start to end in source array is huge. It will throw "RangeError: Maximum call stack size exceeded"... as it will exceed the maximum allowed number of arguments in a function. (try demo script here) "

7 Answers

A combination of slice and splice is probably the way to go here. You can write a helper function that mimics the same behavior in other languages. Here's one that mirrors Java's arraycopy method:

const arr1 = [1, 2, 3, 4, 5, 6];
const arr2 = ["a", "b", "c", "d", "e", "f"];

function arrayCopy(src, srcIndex, dest, destIndex, length) {
  dest.splice(destIndex, length, ...src.slice(srcIndex, srcIndex + length));
}

// copy from arr1, at position 0, into arr2, at position 2, 3 elements.
arrayCopy(arr1, 0, arr2, 2, 3);
console.log(arr2)

Nothing similar in JavaScript. There might be JavaScript libraries with similar functionality, but I am not aware of any. Simple loop and assignment will be much more efficient, as it avoids the overhead from function calls and making new arrays :

function arraycopy(src, srcPos, dst, dstPos, length) {
    while (length--) dst[dstPos++] = src[srcPos++]; return dst;
}

console.log( arraycopy([2,3,4,5,6], 1, [1,1,1,1,1,1], 2, 3) )


Another inefficient alternative for completeness, is copying the values with Object.assign :

function arraycopy(src, srcPos, dst, dstPos, length) {
    return Object.assign(dst, Array(dstPos).concat(src.slice(srcPos, srcPos + length)))
}

console.log( arraycopy([2,3,4,5,6], 1, [1,1,1,1,1,1], 2, 3) )

Not a single function that I know of, but I think this functionality can be achieved using slice() and splice().

The slice() method slices out a piece of an array into a new array.

The splice() method can be used to add new items to an array.

You can use the Array.prototype.splice() (along with the method Array.prototype.slice()) method to do exactly what you are describing.

Say you want to copy from index 3 to 5 (inclusive) out of array A into index 3 of array B (replacing anything in B from index 3 to 5):

const A = [0, 1, 2, 3, 4, 5, 6];
const B = ['a', 'b', 'c', 'd'];

const start = 3;
const end = 6;

// Splice (end - start) elements starting at 3
// with elements from start to end in A
B.splice(3, end - start, ...A.slice(start, end));

console.log(B);

Note: this also takes advantage of JavaScript's spread operator which emits all individual values from the array returned by A.slice().

Edit

Taking the following into consideration:

Update 1: It should exactly only copy and not add or remove elements from any of the two given arrays.

and

...it will break, if the range start to end in source array is huge.

Consider the following function:

function arrayCopy(arr1, index1, arr2, index2, length) {
    return []
        .concat(arr2.slice(0, index2))                  // Array 2 from beginning until the index to replace
        .concat(arr1.slice(index1, index1 + length))    // Array 1 from the index to copy for 'length' elements
        .concat(arr2.slice(index2 + length));           // Rest of Array 2, 'length' elements past the index to replace
}

const A = [0, 1, 2, 3, 4, 5, 6];
const B = ['a', 'b', 'c', 'd'];

// Copy B into Z and replace Z[3] to Z[5] with A[3] to A[5]
const Z = arrayCopy(A, 3, B, 3, 3);

console.log('\'A\' after copy:');
console.log(A);
console.log('\'B\' after copy:');
console.log(B);
console.log('\'Z\' after copy:');
console.log(Z);

// Testing the method with arrays of length 2,000,000

const longArray = [];
const otherLongArray = [];

for (let i = 0; i < 2000000; i++) {
    longArray.push(i);
    otherLongArray.push(-i);
}

const newLongArray = arrayCopy(longArray, 0, otherLongArray, 1500000, 1000000);

console.log(newLongArray.length);

This will create a new array that is a copy of the second input array, but replaces length number of elements in that array (starting at index2) with length number of elements from the first input array (starting at index1). The function utilizes Array.prototype.concat() which takes a full array as an argument. This should alleviate the "callstack" error problem when the array is really large.

Another approach would be to use slice to cut out the elements you need from the source array (without altering it), then forEach to replace elements from the distination array with the cut out elements:

function arraycopy(src, srcPos, dst, dstPos, length) {
    src.slice(srcPos, srPos + length)
       .forEach((e, i) => dst[dstPos + i] = e);
}

Example:

function arraycopy(src, srcPos, dst, dstPos, length) {
    src.slice(srcPos, srcPos + length)
       .forEach((e, i) => dst[dstPos + i] = e);
}

let arr1 = [0, 1, 2, 3, 4, 5, 6];
let arr2 = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];

arraycopy(arr1, 2, arr2, 3, 3);

console.log("arr1: " + arr1);
console.log("arr2: " + arr2);

JavaScript Array slice() and concat() Method fits your requirement.

var arr= ["a", "b", "c", "d", "e"];
var arr2 = ["x","y","z"];
arr= arr.slice(0, index3).concat(arr2.slice(index1: index2+1)).concat(arr.slice(index4+1, arr.length));

HTML file for trial

You can use the slice method to copy a range of elements from one array to another, and keeps to original array untouched.

The slice method accepts two arguments, the first one is the start index of the copy operation, the second argument is the end index of the copy operation, the end index is not included in the result array.

Here's an illustration of what being said:

var arr1 = ['Hi ', 'there! ', 'How ', 'are ', 'you ?'];
var arr2 = arr1.slice(2, arr1.length);
console.log(arr2.toString());
// output: How ,are ,you ?

Learn more about slice method.

Related