Javascript - вставити масив всередину іншого масиву


87

Який найефективніший спосіб вставити масив всередину іншого масиву.

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

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

Ітерація a2 з використанням сплайсингу виглядає трохи неймовірно з точки зору продуктивності, якщо масив a2 великий.

Дякую.



2
це не один елемент, а масив, тому сплайсинг не працює
ic3

Відповіді:


179

Ви можете використовувати в 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 10000 разів (так що в підсумку ви отримаєте масив з 20 005 елементами). Цей метод прийняв: 81ms , метод скибочку + CONCAT прийняв 156ms (перевірено на Chrome 13) . Звичайно, це має стандартне застереження, що в більшості випадків розбірливість буде важливішою за швидкість.
nickf

3
@nick: У chrome, якщо масиви містять понад 130000 елементів, applyбуде вилучено виняток stackoverflow . jsfiddle.net/DcHCY Також відбувається в JSPerf я вірю в FF і IE порогу близько 500к
Неа

чому ви використовуєте apply на відміну від прямого виклику сплайсингу та передачі аргументів?
Пітер П.

@ FrançoisWahl, це серйозне питання, яке люди часто ігнорують у своїх відповідях. Я взяв ваш приклад і змусив його працювати на північ від 1М елементів: stackoverflow.com/a/41466395/1038326
Габріель Коен,

Я щойно спробував це (червень 2017 р.), І найшвидшим способом як для малих розмірів масивів, так і для великих розмірів масивів (у Chrome, FF та Edge) є target.slice(0, insertIndex).concat(insertArr, target.slice(insertIndex)); jsperf.com/inserting-an-array-within-an-array
Alex Діма


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)[docs] для додавання 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

Мені це більше подобається
Кімчі Ман

Підсумовуючи, движок JS повинен повернути чотири масиви до закінчення виразу. Мені подобається логіка рішення, але вона може бути не оптимальною з точки зору простору. Однак я погоджуюсь, що він чудовий і дуже сумісний з браузерами. Використання оператора поширення коштує витрат, оскільки він працює на колекціях, але я міг би взяти це за повернення чотирьох масивів у довгостроковій перспективі.
Ентоні Ратледж,

3

Оператор поширення дозволяє розширити вираз у місцях, де очікується наявність кількох аргументів (для викликів функцій) або декількох елементів (для літералів масиву).

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


3

Тут є кілька справді творчих відповідей на це питання. Ось просте рішення для тих, хто тільки починає з масивів. За бажанням його можна працювати аж до браузерів, сумісних із ECMAScript 3.

Дізнайтеся щось про сплайсинг перед початком роботи.

Мережа розробників Mozilla: 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()!!!!

Примітка: Використання оператора розповсюдження ...робить все це набагато простішим у виконанні.


2

Я хотів знайти спосіб зробити це за допомогою splice()ітерації без жодної ітерації: 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

.concatповертає новий масив, він не змінює існуючий, як splice. Має бути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

Ось моя версія без особливих хитрощів:

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

У цьому випадку може бути простіше просто .reverse () масив new_values, замість того, щоб збільшувати insert_index для кожної ітерації. Звичайно, коли start_index дорівнює нулю або start_index - 1, ефективність цього рішення погана, порівняйте з просто використанням .concat () без явних циклів або unshift () або push () `з .apply().
Ентоні Ратледж,

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)я придумав щось простіше, ніж те, що ви пропонуєте. Однак проблема полягає у вставці масиву між межами масиву, а не виключно в додаванні масиву до початку чи кінця. ;-)
Ентоні Ратледж,

0

Як згадувалося в іншому потоці, відповіді вище не працюватимуть у дуже великих масивах (200 тис. Елементів). Дивіться тут альтернативну відповідь із сплайсингом та ручним натисканням: 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]);
       }
    }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.