Javascript - แทรกอาร์เรย์ภายในอาร์เรย์อื่น


91

อะไรคือวิธีที่มีประสิทธิภาพมากกว่าในการแทรกอาร์เรย์ภายในอาร์เรย์อื่น

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

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

การทำซ้ำ a2 โดยใช้ splice ดูแย่ไปหน่อยจากมุมมองประสิทธิภาพหากอาร์เรย์ a2 มีขนาดใหญ่

ขอบคุณ.



2
ไม่ใช่รายการเดียว แต่เป็นอาร์เรย์ดังนั้น
Splice

คำตอบ:


182

คุณสามารถใช้spliceร่วมกับapplyกลอุบายบางอย่าง:

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

ใน ES2015 + คุณสามารถใช้ตัวดำเนินการกระจายแทนเพื่อทำให้สิ่งนี้ดีขึ้นเล็กน้อย

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

10
@icCube: ฉันเพิ่งใช้เกณฑ์มาตรฐานเปรียบเทียบวิธีนี้กับคำตอบของแพทริค เริ่มต้นด้วย a1 และ a2 ตามในตัวอย่างและฉีดตัวแปร a2 ลงใน a1 10,000 ครั้ง (ดังนั้นในท้ายที่สุดคุณจะมีอาร์เรย์ที่มีองค์ประกอบ 20,005) วิธีการนี้ใช้เวลา: 81msวิธีชิ้น + concat เอา156ms (ทดสอบบน Chrome 13) แน่นอนว่าสิ่งนี้มาพร้อมกับข้อแม้มาตรฐานที่ในกรณีส่วนใหญ่ความชัดเจนจะสำคัญกว่าความเร็ว
nickf

3
@nick: ใน chrome ถ้าอาร์เรย์มีมากกว่า 130000 รายการapplyจะทำให้เกิดข้อยกเว้น stackoverflow jsfiddle.net/DcHCYยังเกิดขึ้นใน jsPerf ฉันเชื่อใน FF และ IE ขีด จำกัด อยู่ที่ประมาณ 500k
Nope

เหตุใดคุณจึงใช้ apply แทนการเรียก splice โดยตรงและส่งผ่านข้อโต้แย้ง
Peter P.

@ FrançoisWahlนี่เป็นปัญหาร้ายแรงที่ผู้คนมักเพิกเฉยต่อคำตอบของพวกเขา ฉันได้ใช้ตัวอย่างของคุณและทำให้มันใช้งานได้กับองค์ประกอบทางเหนือของ 1M: stackoverflow.com/a/41466395/1038326
Gabriel Kohen

ฉันเพิ่งลองสิ่งนี้ (มิถุนายน 2017) และวิธีที่เร็วที่สุดสำหรับทั้งขนาดอาร์เรย์ขนาดเล็กและขนาดอาร์เรย์ขนาดใหญ่ (บน Chrome, FF และ Edge) ดูเหมือนจะเป็นtarget.slice(0, insertIndex).concat(insertArr, target.slice(insertIndex)); jsperf.com/inserting-an-array-within-an-array
Alex Dima

18

ตอนนี้คุณสามารถทำได้หากใช้ ES2015 หรือใหม่กว่า:

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]

โปรดดูเอกสารประกอบเกี่ยวกับตัวดำเนินการสเปรด (... ) https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator


14

มันผิดในตอนแรก ควรมีการใช้concat()แทน

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

ตัวอย่าง: http://jsfiddle.net/f3cae/1/

นิพจน์นี้ใช้slice(0, 2)[docs]เพื่อส่งคืนสององค์ประกอบแรกของa1( 0ดัชนีเริ่มต้นอยู่ที่ไหนและ2เป็นองค์ประกอบ deleteCount แม้ว่าa1จะไม่มีการเปลี่ยนแปลง)

ผลลัพธ์ระดับกลาง :[1,2]

จากนั้นใช้concat(a2)[เอกสาร]เพื่อต่อa2ท้ายไฟล์[1,2].

ผลลัพธ์ระดับกลาง : [1,2,21,22].

ถัดไปa1.slice(2)จะเรียกว่าภายในต่อท้ายที่ปลายหางของสำนวนนี้ซึ่งจะมีจำนวน.concat()[1,2,21,22].concat(a1.slice(2))

การเรียกไปที่slice(2)ซึ่งมีอาร์กิวเมนต์จำนวนเต็มบวกจะส่งคืนองค์ประกอบทั้งหมดหลังจากองค์ประกอบที่ 2 โดยนับด้วยจำนวนธรรมชาติ (เช่นเดียวกับมีห้าองค์ประกอบดังนั้น[3,4,5]จะส่งคืนจากa1) อีกวิธีหนึ่งในการบอกว่านี่คืออาร์กิวเมนต์ดัชนีจำนวนเต็มเอกพจน์จะบอกว่าa1.slice()ตำแหน่งใดในอาร์เรย์ที่จะเริ่มส่งคืนองค์ประกอบจาก (ดัชนี 2 เป็นองค์ประกอบที่สาม)

ผลลัพธ์ระดับกลาง :[1,2,21,22].concat([3,4,5])

ในที่สุดอันที่สอง.concat()จะเพิ่ม[3,4,5]ส่วนท้ายของ[1,2,21,22].

ผลลัพธ์ :[1,2,21,22,3,4,5]

อาจเป็นเรื่องยากที่จะแก้ไขArray.prototypeแต่เราสามารถขยายวัตถุ Array โดยใช้การสืบทอดต้นแบบและฉีดวัตถุใหม่ดังกล่าวลงในโครงการของคุณ

อย่างไรก็ตามสำหรับผู้ที่อาศัยอยู่ริม ...

ตัวอย่าง: 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 );

ฉันได้รับ a1 6 รายการที่ไม่ใช่ 7 -> [1,2, [21,22], 3,4,5]
ic3

ได้ผล! ขอบคุณมากรอสักวัน แต่นี่เป็นคำตอบที่ดีที่สุด ... แปลกที่พวกเขาไม่มีฟังก์ชัน js สำหรับสิ่งนี้
ic3

ชอบอันนี้ดีกว่า
Kimchi Man

โดยสรุปแล้ว JS engine ต้องส่งคืนอาร์เรย์สี่อาร์เรย์ก่อนที่นิพจน์จะเสร็จสิ้น ฉันชอบตรรกะของการแก้ปัญหา แต่อาจไม่เหมาะสมในแง่ของการพิจารณาพื้นที่ อย่างไรก็ตามฉันยอมรับว่าดีและเข้ากันได้กับเบราว์เซอร์ข้าม มีค่าใช้จ่ายในการใช้ตัวดำเนินการสเปรดเนื่องจากทำงานกับคอลเลกชัน แต่ฉันอาจใช้เวลามากกว่าการส่งคืนอาร์เรย์สี่อาร์เรย์ในระยะยาว
Anthony Rutledge

4

ตัวดำเนินการกระจายช่วยให้นิพจน์สามารถขยายได้ในตำแหน่งที่คาดว่าจะมีอาร์กิวเมนต์หลายตัว (สำหรับการเรียกใช้ฟังก์ชัน) หรือหลายองค์ประกอบ (สำหรับตัวอักษรอาร์เรย์)

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


3

ฉันต้องการที่จะหาวิธีที่จะทำเช่นนี้กับsplice()และไม่มี 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];

ในรูปแบบฟังก์ชันวัตถุประสงค์ทั่วไป:

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
}

สิ่งนี้จะปรับเปลี่ยนa2อาร์เรย์เช่นกันซึ่งอาจไม่เป็นที่พึงปรารถนา นอกจากนี้คุณไม่จำเป็นต้องไปที่Array.prototypeเมื่อคุณมีฟังก์ชั่นชิ้นขวามีหรือa1 a2
nickf

ฉันรู้ว่ามันกำลังแก้ไข a2 OP สามารถตัดสินใจได้ว่าเป็นปัญหาหรือไม่ มีอะไรผิดปกติในการไปที่ต้นแบบเพื่อรับวิธีการ? ดูเหมือนจะเหมาะสมกับฉันมากขึ้นเนื่องจากฉันแค่ต้องการวิธีการและวัตถุกำลังถูกเพิ่มในapply()วิธีการ
jfriend00

ไม่เลยมันเป็นฟังก์ชันเดียวกัน แต่อันที่สั้นกว่า: Array.prototype.splicevs a1.splice:)
nickf

เพิ่มฟังก์ชันการแทรกอาร์เรย์เอนกประสงค์
jfriend00

.concatspliceกลับอาร์เรย์ใหม่ก็ไม่ได้ปรับเปลี่ยนที่มีอยู่เช่น ควรจะเป็นargs = args.concat(arrayToInsert);
nickf

3

มีคำตอบที่สร้างสรรค์อย่างแท้จริงสำหรับคำถามนี้ที่นี่ นี่คือวิธีง่ายๆสำหรับผู้ที่เพิ่งเริ่มต้นด้วยอาร์เรย์ สามารถทำให้ทำงานได้จนถึงเบราว์เซอร์ที่เข้ากันได้กับ ECMAScript 3 หากต้องการ

รู้บางอย่างเกี่ยวกับการประกบกันก่อนเริ่มต้น

Mozilla Developer Network: Array.prototype.splice ()

.splice()ครั้งแรกเข้าใจทั้งสองรูปแบบที่สำคัญของ

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

วิธีที่ 1) ลบองค์ประกอบ x (deleteCount) โดยเริ่มจากดัชนีที่ต้องการ

let startIndex = 0, 
    deleteCount = 2;

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

วิธีที่ 2) ลบองค์ประกอบหลังจากดัชนีเริ่มต้นที่ต้องการไปยังจุดสิ้นสุดของอาร์เรย์

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

การใช้.splice()เป้าหมายสามารถแบ่งออกa1เป็นอาร์เรย์หัวและหางโดยใช้หนึ่งในสองรูปแบบด้านบน

เมื่อใช้วิธี # 1 ค่าส่งคืนจะกลายเป็นส่วนหัวและa1ส่วนท้าย

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

ตอนนี้ในบัดดลให้ต่อหัวลำตัว ( a2) และหางเข้าด้วยกัน

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

ดังนั้นการแก้ปัญหานี้จึงเหมือนโลกแห่งความจริงมากกว่าที่อื่น ๆ ที่นำเสนอจนถึงตอนนี้ นี่ไม่ใช่สิ่งที่คุณจะทำกับ Legos หรือ? ;-) นี่คือฟังก์ชั่นที่ทำโดยใช้วิธี # 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]

สั้นกว่า:

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

ปลอดภัยกว่า:

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

ข้อดีของโซลูชันนี้ ได้แก่ :

1) หลักฐานง่ายๆครอบงำโซลูชัน - เติมอาร์เรย์ว่าง

2) ระบบการตั้งชื่อศีรษะลำตัวและหางให้ความรู้สึกเป็นธรรมชาติ

3) ไม่มีการโทรซ้ำ .slice()ซ้ำ ไม่มีการหั่นเลย

4) ไม่ .apply()ไม่มี ไม่จำเป็นอย่างยิ่ง

5) หลีกเลี่ยงการผูกมัดวิธีการ

6) ทำงานใน ECMAScript 3 และ 5 เพียงแค่ใช้varแทนletหรือconstหรือ

** 7) ตรวจสอบให้แน่ใจว่าจะมีหัวและหางที่จะตบเข้ากับร่างกายซึ่งแตกต่างจากโซลูชันอื่น ๆ ที่นำเสนอ หากคุณกำลังเพิ่มอาร์เรย์ก่อนหรือหลังขอบเขตอย่างน้อยคุณควรใช้.concat()!!!!

หมายเหตุ: การใช้ตัวดำเนินการการแพร่กระจาย...ทำให้ทั้งหมดนี้สำเร็จได้ง่ายขึ้นมาก


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

นี่คือเวอร์ชันของฉันที่ไม่มีเทคนิคพิเศษ:

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

ในกรณีนี้อาจทำได้ง่ายกว่าเพียงแค่. ย้อนกลับ () อาร์เรย์ new_values ​​แทนที่จะเพิ่ม insert_index สำหรับการวนซ้ำแต่ละครั้ง แน่นอนเมื่อ START_INDEX เป็นศูนย์หรือ START_INDEX - 1, ประสิทธิภาพในการแก้ปัญหานี้ไม่ดีเมื่อเทียบกับเพียงแค่ใช้ .concat () โดยลูปใด ๆ อย่างชัดเจนหรือ unshift () หรือผลักดัน () .apply()`ด้วย
Anthony Rutledge

0

หากคุณต้องการแทรกอาร์เรย์อื่นในอาร์เรย์โดยไม่ต้องสร้างอาร์เรย์ใหม่วิธีที่ง่ายที่สุดคือใช้อย่างใดอย่างหนึ่งpushหรือunshiftด้วยapply

เช่น:

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

ใช้งานได้เนื่องจากทั้งสองpushและunshiftรับอาร์กิวเมนต์เป็นจำนวนตัวแปร โบนัสคุณสามารถเลือกได้อย่างง่ายดายว่าจะต่อท้ายอาร์เรย์จากไหน!


เพียงแค่ทำa1.concat(a2)หรือa2.concat(a1)ฉันคิดบางอย่างที่ง่ายกว่าที่คุณแนะนำ อย่างไรก็ตามปัญหาคือการแทรกอาร์เรย์ระหว่างขอบเขตอาร์เรย์ไม่ใช่เฉพาะการเพิ่มอาร์เรย์ไปยังจุดเริ่มต้นหรือจุดสิ้นสุด ;-)
Anthony Rutledge

0

ดังที่กล่าวไว้ในเธรดอื่นคำตอบข้างต้นจะไม่ทำงานในอาร์เรย์ขนาดใหญ่มาก (องค์ประกอบ 200K) ดูคำตอบอื่นเกี่ยวกับการประกบและการกดด้วยตนเองได้ที่นี่: 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]);
       }
    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.