Javascript - chèn một mảng vào bên trong một mảng khác


87

Cách hiệu quả hơn để chèn một mảng bên trong một mảng khác là gì.

a1 = [1,2,3,4,5];
a2 = [21,22];

newArray - a1.insertAt(2,a2) -> [1,2, 21,22, 3,4,5];

Lặp lại a2 bằng cách sử dụng splice trông hơi tệ từ quan điểm hiệu suất nếu mảng a2 lớn.

Cảm ơn.



2
nó không phải là một mục mà là một mảng, vì vậy mối nối không hoạt động
ic3

Câu trả lời:


179

Bạn có thể sử dụng splicekết hợp với một số applythủ thuật:

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];

Trong ES2015 +, bạn có thể sử dụng toán tử spread để làm cho điều này đẹp hơn một chút

a1.splice(2, 0, ...a2);

10
@icCube: Tôi vừa chạy một điểm chuẩn, so sánh phương pháp này với phương pháp trong câu trả lời của Patrick. Nó bắt đầu bằng a1 và a2 như trong ví dụ, và đưa biến a2 vào a1 10.000 lần (để cuối cùng, bạn có một mảng có 20.005 phần tử). Phương pháp này mất: 81ms , phương pháp slice + concat mất 156ms (thử nghiệm trên Chrome 13) . Tất nhiên, điều này đi kèm với cảnh báo tiêu chuẩn rằng trong hầu hết các trường hợp, tính dễ đọc sẽ quan trọng hơn tốc độ.
nickf

3
@nick: Trong chrome nếu các mảng chứa hơn 130000 mục applysẽ ném ra một ngoại lệ stackoverflow. jsfiddle.net/DcHCY Cũng xảy ra trong jsPerf Tôi tin rằng trong FF và IE, thành trì là khoảng 500k
Không.

tại sao bạn sử dụng apply thay vì gọi splice trực tiếp và truyền các đối số?
Peter P.

@ FrançoisWahl, đây là một vấn đề nghiêm trọng mà mọi người thường bỏ qua trong câu trả lời của họ. Tôi đã lấy ví dụ của bạn và làm cho nó hoạt động với phía bắc của 1 triệu phần tử: stackoverflow.com/a/41466395/1038326
Gabriel Kohen

Tôi vừa thử điều này (tháng 6 năm 2017) và cách nhanh nhất cho cả kích thước mảng nhỏ và kích thước mảng lớn (trên Chrome, FF và Edge) dường như là target.slice(0, insertIndex).concat(insertArr, target.slice(insertIndex)); jsperf.com/inserting-an-array-within-an-array
Alex Dima


14

Đã có nó sai lúc đầu. Nên đã sử dụng concat()thay thế.

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));

Ví dụ: http://jsfiddle.net/f3cae/1/

Biểu thức này sử dụng slice(0, 2)[docs] để trả về hai phần tử đầu tiên của a1(đâu 0là chỉ mục bắt đầu và 2là phần tử deleteCount, mặc dù a1không bị thay đổi).

Kết quả trung gian :[1,2]

Sau đó, nó sử dụng concat(a2)[docs] để nối a2vào cuối [1,2].

Kết quả trung gian : [1,2,21,22].

Tiếp theo, a1.slice(2)được gọi trong một dấu .concat()ở cuối đuôi của biểu thức này, có giá trị [1,2,21,22].concat(a1.slice(2)).

Một lệnh gọi tới slice(2), có đối số là số nguyên dương, sẽ trả về tất cả các phần tử sau phần tử thứ 2, đếm theo số tự nhiên (như trong có năm phần tử, vì vậy [3,4,5]sẽ được trả về từ a1). Một cách khác để nói điều này là đối số chỉ số số nguyên số ít cho biết a1.slice()vị trí nào trong mảng để bắt đầu trả về các phần tử từ (chỉ số 2 là phần tử thứ ba).

Kết quả trung gian :[1,2,21,22].concat([3,4,5])

Cuối cùng, thứ hai .concat()thêm [3,4,5]vào cuối [1,2,21,22].

Kết quả :[1,2,21,22,3,4,5]

Nó có thể hấp dẫn để thay đổi Array.prototype, nhưng người ta có thể chỉ cần mở rộng đối tượng Array bằng cách sử dụng kế thừa nguyên mẫu và đưa đối tượng mới vào các dự án của bạn.

Tuy nhiên, đối với những người sống ở rìa ...

Ví dụ: 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 );

Tôi nhận được a1 với 6 mặt hàng không 7 -> [1,2, [21,22], 3,4,5]
IC3

làm ! cảm ơn rất nhiều, chúng ta hãy chờ một vài ngày để nhưng đây là câu trả lời tốt nhất ... kỳ lạ là họ không có chức năng js cho điều này.
ic3

Tôi thích cái này hơn
Kimchi Man

Tóm lại, công cụ JS phải trả về bốn mảng trước khi biểu thức kết thúc. Tôi thích tính logic của giải pháp, nhưng nó có thể không tối ưu về mặt không gian. Tuy nhiên tôi đồng ý rằng nó tiện lợi và rất tương thích với nhiều trình duyệt. Có một khoản chi phí để sử dụng toán tử spread vì nó hoạt động trên các bộ sưu tập, nhưng tôi có thể sử dụng điều đó để trả về bốn mảng về lâu dài.
Anthony Rutledge

3

Các lây lan khai thác cho phép một biểu thức để được mở rộng ở những nơi nhiều đối số (đối với các cuộc gọi chức năng) hoặc nhiều thành phần (ví literals mảng) được mong đợi.

a2 = [21,22];
a1 = [1,2,...a2,3,4,5];//...a2 is use of spread operator
console.log(a1);


3

Có một số câu trả lời thực sự sáng tạo cho câu hỏi này ở đây. Đây là một giải pháp đơn giản cho những người mới bắt đầu với mảng. Nó có thể được thực hiện để hoạt động trên tất cả các trình duyệt tuân thủ ECMAScript 3, nếu muốn.

Biết một số điều về mối nối trước khi bắt đầu.

Mạng nhà phát triển Mozilla: Array.prototype.splice ()

Trước tiên, hãy hiểu hai hình thức quan trọng của .splice().

let a1 = [1,2,3,4],
    a2 = [1,2];

Phương pháp 1) Loại bỏ x (deleteCount) phần tử, bắt đầu từ một chỉ mục mong muốn.

let startIndex = 0, 
    deleteCount = 2;

a1.splice(startIndex, deleteCount); // returns [1,2], a1 would be [3,4]

Phương pháp 2) Loại bỏ các phần tử sau chỉ số bắt đầu mong muốn đến cuối mảng.

a1.splice(2); // returns [3,4], a1 would be [1,2]

Khi sử dụng .splice(), mục tiêu có thể là chia a1thành mảng đầu và mảng bằng cách sử dụng một trong hai dạng trên.

Sử dụng phương pháp # 1, giá trị trả về sẽ trở thành phần đầu và a1phần đuôi.

let head = a1.splice(startIndex, deleteCount); // returns [1,2], a1 would be [3,4]

Bây giờ, trong một cú ngã sà xuống, hãy nối đầu, thân ( a2) và đuôi

[].concat(head, a2, a1);

Vì vậy, giải pháp này giống với thế giới thực hơn bất kỳ giải pháp nào khác được trình bày cho đến nay. Đây không phải là những gì bạn sẽ làm với Legos? ;-) Đây là một hàm, được thực hiện bằng phương pháp # 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]

Ngắn hơn:

/**
*@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));
}

An toàn hơn:

/**
*@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));
}

Những ưu điểm của giải pháp này bao gồm:

1) Một tiền đề đơn giản chiếm ưu thế trong giải pháp - điền vào một mảng trống.

2) Danh pháp đầu, thân và đuôi cảm thấy tự nhiên.

3) Không có cuộc gọi kép tới .slice(). Không cắt chút nào.

4) Không .apply(). Rất không cần thiết.

5) Chuỗi phương pháp được tránh.

6) Hoạt động trong ECMAScript 3 và 5 đơn giản bằng cách sử dụng varthay thế cho lethoặc const.

** 7) Đảm bảo rằng sẽ có đầu và đuôi để tát vào cơ thể, không giống như nhiều giải pháp khác được trình bày. Nếu bạn đang thêm một mảng trước hoặc sau, giới hạn, ít nhất bạn nên sử dụng .concat()!!!!

Lưu ý: Việc sử dụng công cụ quảng cáo lan truyền ...làm cho tất cả những điều này trở nên dễ dàng hơn nhiều.


2

Tôi muốn tìm cách thực hiện việc này mà splice()không cần lặp lại: 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];

Ở dạng hàm mục đích chung:

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
}

Điều này cũng sửa đổi a2mảng, điều này có lẽ khá không mong muốn. Ngoài ra, bạn không cần phải đi đến Array.prototypekhi bạn có chức năng lát ngay tại đó a1hoặc a2.
nickf

Tôi biết rằng nó đang sửa đổi a2. OP có thể quyết định xem đó có phải là vấn đề hay không. Có gì sai khi đi tới nguyên mẫu để lấy phương thức không? Nó có vẻ phù hợp hơn với tôi vì tôi chỉ muốn phương thức và đối tượng đang được thêm vào apply()phương thức.
jfriend00

Cũng không có, đó là chức năng tương tự chính xác, nhưng là một trong ngắn: Array.prototype.splicevs a1.splice:)
nickf

Đã thêm chức năng chèn mảng mục đích chung.
jfriend00

.concattrả về một mảng mới, nó không sửa đổi mảng hiện có như splice. Nên làargs = args.concat(arrayToInsert);
nickf

0
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);

0

Đây là phiên bản của tôi không có thủ thuật đặc biệt:

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;
}

Trong trường hợp này, có thể đơn giản hơn chỉ cần .reverse () mảng new_values, thay vì tăng insert_index cho mỗi lần lặp. Tất nhiên, khi start_index bằng 0 hoặc start_index - 1, hiệu quả của giải pháp này là kém, so với việc chỉ sử dụng .concat () mà không có bất kỳ vòng lặp rõ ràng nào, hoặc unshift () hoặc push () `với .apply().
Anthony Rutledge

0

Nếu bạn muốn chèn một mảng thành một mảng mà không cần tạo một hình mới, cách dễ nhất là sử dụng một trong hai pushhoặc unshiftvớiapply

Ví dụ:

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);

Điều này hoạt động vì cả hai pushunshiftlấy một số đối số thay đổi. Một phần thưởng, bạn có thể dễ dàng chọn đầu nào để gắn mảng từ đó!


Chỉ bằng cách làm a1.concat(a2)hoặc a2.concat(a1)tôi đã nghĩ ra một cái gì đó dễ dàng hơn những gì bạn đề xuất. Tuy nhiên, vấn đề là chèn một mảng giữa các giới hạn của mảng, không chỉ thêm một mảng vào đầu hoặc cuối. ;-)
Anthony Rutledge

0

Như đã đề cập trong một chủ đề khác, các câu trả lời ở trên sẽ không hoạt động trong các mảng rất lớn (200K phần tử). Xem câu trả lời thay thế ở đây liên quan đến mối nối và đẩy thủ công: 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]);
       }
    }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.