Як вставити елемент у масив із певним індексом (JavaScript)?


2931

Я шукаю метод вставлення масиву JavaScript у стилі:

arr.insert(index, item)

Переважно в jQuery, але будь-яка реалізація JavaScript буде робити в цей момент.


95
Зауважте, що JQuery - це бібліотека маніпуляцій з DOM та подіями, а не власна мова. Це не має нічого спільного з маніпулюванням масивом.
Доміно

25
api.jquery.com/jQuery.inArray не має нічого спільного з DOM або подіями. jQuery перетворився на змішаний набір інструментів для розробки JS браузера, що призвело до того, що люди очікують, що в ньому є метод для всього.
Тім

4
@Tim, але це все ще не є власною мовою (все ж є деякі питання на кшталт "як підбити два числа в jQuery" тут на SO)
Віктор

3
@Victor Ні, і ніколи не буде. jQuery був корисним та релевантним, але в нього був свій день.
Тім

1
Також, дивіться це , на кручений сюрприз (:
noobie

Відповіді:


4755

Що ви хочете, це spliceфункція на об'єкті власного масиву.

arr.splice(index, 0, item);буде вставлений itemу arrвказаний індекс ( 0спочатку видалення елементів, тобто це лише вставка).

У цьому прикладі ми створимо масив і додамо до нього елемент у індекс 2:

var arr = [];
arr[0] = "Jani";
arr[1] = "Hege";
arr[2] = "Stale";
arr[3] = "Kai Jim";
arr[4] = "Borge";

console.log(arr.join());
arr.splice(2, 0, "Lene");
console.log(arr.join());


168
Дякую, я подумав, що почуватимусь дурним за запитання, але тепер, коли знаю відповідь, я цього не роблю! Чому на землі вони вирішили назвати його зрощенням, коли для тієї ж функції використовується загальний термін пошуку ?!
tags2k

83
@ tags2k: адже ця функція робить більше, ніж вставляти елементи, а її ім'я вже встановлено в perl?
Крістоф


55
Сплай можна вставляти, але так само часто не можна . Наприклад: arr.splice(2,3)буде видалено 3 елементи, починаючи з індексу 2. Не проходячи 3-го .... Nth параметрів нічого не вставляється. Тож ім'я теж insert()не справедливо.
EBarr

14
Я думаю, що термін "зрощення" має сенс. Сплайс - означає приєднатися або з'єднатися, також змінити. У вас встановлений масив, який ви зараз "змінюєте", який би передбачав додавання або видалення елементів. Ви вказуєте, з якого масиву потрібно починати, потім скільки старих елементів, які потрібно видалити (якщо такі є), і нарешті, необов'язково список нових елементів, які потрібно додати. Сплайс - це також великий науково-фантастичний термін.
Якуб Келлер

286

Ви можете реалізувати Array.insertметод, зробивши це:

Array.prototype.insert = function ( index, item ) {
    this.splice( index, 0, item );
};

Тоді ви можете використовувати його так:

var arr = [ 'A', 'B', 'D', 'E' ];
arr.insert(2, 'C');

// => arr == [ 'A', 'B', 'C', 'D', 'E' ]

9
Щоб вставити кілька елементів, ви можете використовуватиArray.prototype.insert = function (index, items) { this.splice.apply(this, [index, 0].concat(items)); }
Райан Сміт

7
Проблема з додаванням матеріалів до масиву полягає в тому, що ця функція з’явиться як елемент, коли ви робите для (i in arr) {...}
rep_movsd

7
Але майте на увазі, що не рекомендується розширювати нативні типи, оскільки це може заважати іншому коду чи майбутнім функціоналам.
марш

16
Не змінюйте об’єкти, якими ви не володієте
Луїс Кабрера Беніто,

4
Не змінюйте прототипи
satya164

141

Крім splice, ви можете використовувати такий підхід, який не буде мутувати вихідний масив, але створить новий масив із доданим елементом. Зазвичай слід уникати мутацій, коли це можливо. Тут я використовую ES6-оператор розповсюдження.

const items = [1, 2, 3, 4, 5]

const insert = (arr, index, newItem) => [
  // part of the array before the specified index
  ...arr.slice(0, index),
  // inserted item
  newItem,
  // part of the array after the specified index
  ...arr.slice(index)
]

const result = insert(items, 1, 10)

console.log(result)
// [1, 10, 2, 3, 4, 5]

Це можна використовувати для додавання декількох елементів, трохи налаштувавши функцію, щоб використовувати оператор відпочинку для нових елементів, а також поширити це у поверненому результаті

const items = [1, 2, 3, 4, 5]

const insert = (arr, index, ...newItems) => [
  // part of the array before the specified index
  ...arr.slice(0, index),
  // inserted items
  ...newItems,
  // part of the array after the specified index
  ...arr.slice(index)
]

const result = insert(items, 1, 10, 20)

console.log(result)
// [1, 10, 20, 2, 3, 4, 5]


1
Це хороший, безпечний спосіб зробити це? Я прошу, бо це здається настільки витонченим і стислим, але жодної іншої відповіді на це не стосується. Більшість з них модифікує об'єкт-прототип!
Сувора Канчина

5
@HarshKanchina Це, мабуть, тому, що більшість відповідей попередньо ES6, але цей підхід зараз дуже поширений з мого досвіду
gafi

77

Спеціальні insertметоди масиву

1. За допомогою декількох аргументів та ланцюгової підтримки

/* Syntax:
   array.insert(index, value1, value2, ..., valueN) */

Array.prototype.insert = function(index) {
    this.splice.apply(this, [index, 0].concat(
        Array.prototype.slice.call(arguments, 1)));
    return this;
};

Він може вставляти кілька елементів (як spliceце робить рідний ) та підтримує ланцюжок:

["a", "b", "c", "d"].insert(2, "X", "Y", "Z").slice(1, 6);
// ["b", "X", "Y", "Z", "c"]

2. За допомогою аргументів типу масиву, що об'єднують і підтримують ланцюжок підтримки

/* Syntax:
   array.insert(index, value1, value2, ..., valueN) */

Array.prototype.insert = function(index) {
    index = Math.min(index, this.length);
    arguments.length > 1
        && this.splice.apply(this, [index, 0].concat([].pop.call(arguments)))
        && this.insert.apply(this, arguments);
    return this;
};

Він може об'єднати масиви з аргументів із заданим масивом, а також підтримує ланцюжок:

["a", "b", "c", "d"].insert(2, "V", ["W", "X", "Y"], "Z").join("-");
// "a-b-V-W-X-Y-Z-c-d"

DEMO: http://jsfiddle.net/UPphH/


Чи є компактний спосіб, щоб ця версія також об'єднала масив, коли він знайде один в аргументах?
Ноло

Я не розумію першого результату ["b", "X", "Y", "Z", "c"]. Чому не "d"включений? Мені здається, що якщо ви поставите 6 як другий параметр, slice()а в масиві є 6 елементів, починаючи з вказаного індексу, то вам слід отримати всі 6 елементів у зворотному значенні. (Документ говорить howManyпро цей параметр.) Developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Alexis Wilke

Насправді, якщо я використовую індекс 3 або більше, я нічого не отримую у виході (випадок 1., FireFox) ["a", "b", "c", "d"].insert(2, "X", "Y", "Z").slice(3, 3);=>[ ]
Алексіс Вілке

@AlexisWilke У першому прикладі я використовував sliceметод, а не splice, про який ви посилаєтесь у коментарі. Другий параметр slice(названий end) - нульовий індекс, на якому закінчується видобуток. sliceвитяжки до, але не включаючиend . Тому після того, як у insertвас є ["a", "b", "X", "Y", "Z", "c", "d"], з якого sliceвитягує елементи з індексами від 1до до 6, тобто від "b"до , "d"але не включаючи "d". Чи є сенс?
VisioN

41

Якщо ви хочете вставити кілька елементів у масив одразу, ознайомтеся з відповіддю на переповнення стека: Кращий спосіб сплавити масив у масив у javascript

Ось деякі функції для ілюстрації обох прикладів:

function insertAt(array, index) {
    var arrayToInsert = Array.prototype.splice.apply(arguments, [2]);
    return insertArrayAt(array, index, arrayToInsert);
}

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

Нарешті, ось jsFiddle, щоб ви могли самі це побачити: http://jsfiddle.net/luisperezphd/Wc8aS/

Ось як ви використовуєте функції:

// if you want to insert specific values whether constants or variables:
insertAt(arr, 1, "x", "y", "z");

// OR if you have an array:
var arrToInsert = ["x", "y", "z"];
insertArrayAt(arr, 1, arrToInsert);

1
Не вдасться insertAt () краще зателефонувати insertArrayAt (), коли він створив одноелементний масивToInsert? Це дозволяє уникнути повторення ідентичного коду.
Метт Сах

1
це чудовий приклад того, коли потрібно використовувати «застосувати»
CRice

Я додав параметр removeCount до цього методу, щоб скористатися можливістю сплайсингу також видаляти елементи з цього індексу: Array.prototype.splice.apply (масив, [індекс, видалити | | 0] .concat (arrayToInsert));
CRice

23

Для правильного функціонального цілей програмування та ланцюга важливим є винахід Array.prototype.insert(). Насправді сплайс міг бути ідеальним, якби він повернув мутований масив замість абсолютно безглуздого порожнього масиву. Отож ось це йде

Array.prototype.insert = function(i,...rest){
  this.splice(i,0,...rest)
  return this
}

var a = [3,4,8,9];
document.write("<pre>" + JSON.stringify(a.insert(2,5,6,7)) + "</pre>");

Добре все вище, Array.prototype.splice()один мутує вихідний масив, а деякі можуть скаржитися на кшталт "ви не повинні змінювати те, що вам не належить", і це може виявитися правильним. Тож для громадського добробуту я хотів би подати інший, Array.prototype.insert()який не мутує вихідний масив. Ось це іде;

Array.prototype.insert = function(i,...rest){
  return this.slice(0,i).concat(rest,this.slice(i));
}

var a = [3,4,8,9],
    b = a.insert(2,5,6,7);
console.log(JSON.stringify(a));
console.log(JSON.stringify(b));


2
"абсолютно безглуздий порожній масив" - він повертає порожній масив лише тоді, коли другий параметр дорівнює 0. Якщо він більший за 0, він повертає елементи, вилучені з масиву. Зважаючи на те, що ви додаєте до прототипу і spliceмутуєте оригінальний масив, я не думаю, що "належне функціональне програмування" належить десь поблизу splice.
chrisbajorin

Ми говоримо про вставку тут, а другий параметр Array.prototype.splice () повинен дорівнювати нулю. І те, що воно повертає, не має іншого значення, окрім "я нічого не видалив", і оскільки ми використовуємо його для вставки елемента, ми вже маємо цю інформацію. Якщо ви не хочете мутувати вихідний масив, ви можете зробити те ж саме з двома операціями Array.prototype.slice () та однією операцією Array.prototype.concat (). Тобі вирішувати.
Скорочення

1
Ваша друга реалізація найчистіша з усієї цієї сторінки, і ви маєте нульові голоси. Будь ласка, візьміть мою і продовжуйте гарну роботу. (вам слід просто уникати мутації прототипу, але ви вже це знаєте)
NiKo

1
Думаю, варто згадати, що параметр «відпочинок» - це новий ECMA 6-й ( developer.mozilla.org/en/docs/Web/JavaScript/Reference/… )
Geza Turi

18

Я рекомендую використовувати чистий JavaScript в цьому випадку, також немає методу вставки в JavaScript, але у нас є метод, який є вбудованим методом масиву, який виконує завдання для вас, він називається сплей ...

Давайте подивимось, що зрощують () ...

Метод splice () змінює вміст масиву, видаляючи існуючі елементи та / або додаючи нові елементи.

Гаразд, уявіть, що ми маємо цей масив нижче:

const arr = [1, 2, 3, 4, 5];

Ми можемо видалити 3так:

arr.splice(arr.indexOf(3), 1);

Він поверне 3, але якщо ми перевіримо arr зараз, у нас є:

[1, 2, 4, 5]

Поки що так добре, але як ми можемо додати новий елемент до масиву, використовуючи сплайс? Давайте повернемо 3 в arr ...

arr.splice(2, 0, 3);

Подивимося, що ми зробили ...

Ми знову використовуємо сплайс , але цього разу для другого аргументу ми передаємо 0 , це означає, що ми не хочемо видалити жоден елемент, але в той же час ми додаємо третій аргумент, який є 3, який буде доданий у другому індексі ...

Ви повинні знати, що ми можемо одночасно видаляти та додавати , наприклад, зараз:

arr.splice(2, 2, 3);

Що видалить 2 елементи в індексі 2, потім додасть 3 в індексі 2 і результат буде:

[1, 2, 3, 5];

Тут показано, як працює кожен елемент у сплайсі:

array.splice (start, deleteCount, item1, item2, item3 ...)


13

Додайте окремий елемент у певний індекс

//Append at specific position(here at index 1)
arrName.splice(1, 0,'newName1');
//1: index number, 0: number of element to remove, newName1: new element


//Append at specific position (here at index 3)
arrName[3] = 'newName1';

Додайте кілька елементів у певному індексі

//Append from index number 1
arrName.splice(1, 0,'newElemenet1', 'newElemenet2', 'newElemenet3');
//1: index number from where append start, 
//0: number of element to remove, 
//newElemenet1,2,3: new elements

8
Варто зауважити, що arrName [3] не додається, він переосмислює.
sfratini

Він додає елемент до існуючого масиву не заздалегідь, наприклад: arrName = ['xxx', 'yyy', 'zzz']; arrName.splice (1, 0, 'aaa', 'bbb', 'ccc'); після друку arrName
Шрікрушна

Якщо arrName містить більше 3 елементів, ви переосмислюєте третій, не додаючи. Або я дивлюся на це неправильно?
sfratini

Якщо ми вставимо елемент посередині, він пересуває наступний елемент не зверху. Вкажіть, будь ласка, вашу проблему, що вам потрібно. Візьміть масив (приклад) і те, що вам потрібно на виході. plz прокоментуйте мене
Srikrushna

1
@Srikrushna додається arrName[3] = 'newName1';, якщо масив містить лише 3 елементи. Якщо в індексі 3 є елемент, його буде замінено. Якщо ви хочете додати в кінці кінців, це краще використовуватиarrName.push('newName1');
благоговіння

6

Ще одне можливе рішення з використанням Array#reduce.

var arr = ["apple", "orange", "raspberry"],
    arr2 = [1, 2, 4];

function insert(arr, item, index) {
    arr = arr.reduce(function(s, a, i) {
      i == index ? s.push(item, a) : s.push(a);
      return s;
    }, []);   
    console.log(arr);
}

insert(arr, "banana", 1);
insert(arr2, 3, 2);


6

Ось два способи:

const array = [ 'My', 'name', 'Hamza' ];

array.splice(2, 0, 'is');

console.log("Method 1 : ", array.join(" "));

АБО

Array.prototype.insert = function ( index, item ) {
    this.splice( index, 0, item );
};

const array = [ 'My', 'name', 'Hamza' ];
array.insert(2, 'is');

console.log("Method 2 : ", array.join(" "));


4

Хоча на це вже відповіли, я додаю цю замітку для альтернативного підходу.

Я хотів розмістити відому кількість елементів у масиві, в конкретні позиції, оскільки вони виходять з "асоціативного масиву" (тобто об'єкта), який за визначенням не гарантується в упорядкованому порядку. Я хотів, щоб отриманий масив був масивом об'єктів, але об'єкти мають бути в певному порядку в масиві, оскільки масив гарантує їх порядок. Тому я це зробив.

Спочатку об'єкт-джерело - рядок JSONB, отриманий з PostgreSQL. Я хотів, щоб це було відсортовано за властивістю "порядку" у кожному дочірньому об'єкті.

var jsonb_str = '{"one": {"abbr": "", "order": 3}, "two": {"abbr": "", "order": 4}, "three": {"abbr": "", "order": 5}, "initialize": {"abbr": "init", "order": 1}, "start": {"abbr": "", "order": 2}}';

var jsonb_obj = JSON.parse(jsonb_str);

Оскільки кількість вузлів в об’єкті відома, я спершу створюю масив із заданою довжиною:

var obj_length = Object.keys(jsonb_obj).length;
var sorted_array = new Array(obj_length);

А потім повторіть об'єкт, розмістивши новостворені тимчасові об’єкти у потрібних місцях у масиві, без того, щоб відбуватися «сортування».

for (var key of Object.keys(jsonb_obj)) {
  var tobj = {};
  tobj[key] = jsonb_obj[key].abbr;

  var position = jsonb_obj[key].order - 1;
  sorted_array[position] = tobj;
}

console.dir(sorted_array);

3

Той, хто все ще має проблеми з цим, і спробував усі варіанти, описані вище, і ніколи його не отримав. Я ділюсь своїм рішенням, щоб врахувати, що ви не хочете явно вказати властивості вашого об'єкта проти масиву.

function isIdentical(left, right){
    return JSON.stringify(left) === JSON.stringify(right);
}

function contains(array, obj){
    let count = 0;
    array.map((cur) => {
          if(this.isIdentical(cur, obj)) count++;
    });
    return count > 0;
}

Це комбінація ітерації опорного масиву та порівняння його з об'єктом, який ви хотіли перевірити, перетворити їх обидва в рядок, а потім ітераціювати, якщо він збігався. Тоді можна просто порахувати. Це можна покращити, але саме тут я оселився. Сподіваюсь, це допомагає.


3

Отримання прибутку методу зменшення таким чином:

function insert(arr, val, index) {
    return index >= arr.length 
        ? arr.concat(val)
        : arr.reduce((prev, x, i) => prev.concat(i === index ? [val, x] : x), []);
}

Таким чином, ми можемо повернути новий масив (буде класним функціональним способом - набагато краще, ніж використовувати push або зрощення) з елементом, вставленим в індексі, і якщо індекс більший за довжину масиву, він буде вставлений в кінці.


3

Array#splice()це шлях, якщо ви дійсно не хочете уникати мутування масиву. З огляду на 2 масивів arr1і arr2, ось як ви б вставити вміст arr2в arr1після першого елемента:

const arr1 = ['a', 'd', 'e'];
const arr2 = ['b', 'c'];

arr1.splice(1, 0, ...arr2); // arr1 now contains ['a', 'b', 'c', 'd', 'e']

console.log(arr1)

Якщо вас турбує мутація масиву (наприклад, якщо ви використовуєте Immutable.js), ви можете замість цього використовувати slice(), не плутати splice()з a 'p'.

const arr3 = [...arr1.slice(0, 1), ...arr2, ...arr1.slice(1)];

2

Я спробував це, і це працює чудово!

var initialArr = ["India","China","Japan","USA"];
initialArr.splice(index, 0, item);

Індекс - це місце, куди потрібно вставити або видалити елемент. 0, тобто другий параметр визначає кількість елемента з індексу, що підлягає вилученню, це нові записи, які ви хочете зробити в масиві. Це може бути один або декілька.

initialArr.splice(2, 0, "Nigeria");
initialArr.splice(2, 0, "Australia","UK");

4
просто скопіюйте вклеєну відповідь вище. це не додає ніякого значення питанню. або ви додасте нову відповідь або коментуєте існуючі. будь ласка, спробуйте внести щось нове. ми не хочемо псувати цю спільноту
hannad rehman

1

Ось робоча функція, яку я використовую в одному зі своїх додатків.

Це перевіряє, чи є вихід

let ifExist = (item, strings = [ '' ], position = 0) => {
     // output into an array with empty string. Important just in case their is no item. 
    let output = [ '' ];
    // check to see if the item that will be positioned exist.
    if (item) {
        // output should equal to array of strings. 
        output = strings;
       // use splice in order to break the array. 
       // use positition param to state where to put the item
       // and 0 is to not replace an index. Item is the actual item we are placing at the prescribed position. 
        output.splice(position, 0, item);
    }
    //empty string is so we do not concatenate with comma or anything else. 
    return output.join("");
};

А потім я називаю це нижче.

ifExist("friends", [ ' ( ', ' )' ], 1)}  // output: ( friends )
ifExist("friends", [ ' - '], 1)}  // output:  - friends 
ifExist("friends", [ ':'], 0)}  // output:   friends: 

1

Трохи старіший потік, але я маю згоду зі зменшеним вище, тому що у сплеску напевно є трохи заплутаний інтерфейс. І відповідь, надана cdbajorin, що "він повертає порожній масив лише тоді, коли другий параметр дорівнює 0. Якщо він більший за 0, він повертає елементи, вилучені з масиву", є, хоча й точним, доводить суть. Метою функції є з'єднання або, як раніше говорив Якоб Келлер, "приєднатись або підключитися, також змінити. У вас є встановлений масив, який ви зараз змінюєте, який би передбачав додавання або видалення елементів ...." Враховуючи це, повернути значення повернених елементів, якщо такі є, в кращому випадку незручно. І я на 100% погоджуюся, що цей метод міг би бути більш підходящим для прив'язування ланцюгів, якби він повернув те, що здається природним, додав новий масив із доданими елементами. Тоді ви можете робити такі речі, як ["19", "17"]. Splice (1,0, "18"). Join ("...") або все, що завгодно, з поверненим масивом. Той факт, що він повертає те, що було видалено, - це просто своєрідна дурниця IMHO. Якщо мета методу полягала в тому, щоб "вирізати набір елементів", і це був лише намір, можливо. Схоже, якщо я вже не знаю, що я вирізаю, хоча, мабуть, у мене мало підстав вирізати ці елементи, чи не так? Було б краще, якби він поводився як concat, map, зменшення, зріз тощо, де новий масив створений з існуючого масиву, а не мутування існуючого масиву. Це все можливо, і це важливе питання. Це досить поширене для маніпулювання ланцюговим масивом. Здається, що мова повинна йти в той чи інший бік і намагатися дотримуватися її якомога більше. Javascript є функціональним і менш декларативним, це просто здається дивним відхиленням від норми.


-2

Продуктивність

Сьогодні (2020.04.24) я виконую тести для обраних рішень для великих та малих масивів. Я тестував їх на MacOs High Sierra 10.13.6 на Chrome 81.0, Safari 13.1, Firefox 75.0.

Висновки

Для всіх браузерів

  • дивно, що для невеликих масивів не на місці рішення на основі sliceі reduce(D, E, F) зазвичай на 10x-100x швидше, ніж на місці
  • для великих масивів рішення, що базуються на місці splice(AI, BI, CI), були найшвидшими (іноді ~ 100x - але це залежить від розміру масиву)
  • для невеликих масивів рішення BI було найповільнішим
  • для великих масивів рішення Е було найповільнішим

введіть тут опис зображення

Деталі

Тести були поділені на дві групи: розчини на місці (AI, BI, CI) та не на місці рішення (D, E, F) та виконувались у двох випадках

  • тест на масив з 10 елементами - його можна запустити ТУТ
  • тест на масив з 1.000.000 елементів - ви можете запустити його ТУТ

Тестований код представлений в нижній частині фрагмента

Нижче наведено приклади результатів для малого масиву на хромі

введіть тут опис зображення

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.