Ответ 1
Вы можете использовать Object.assign
:
Object.assign([], array, {2: newItem});
Как выполнить следующую операцию без изменения массива:
let array = ['item1'];
console.log(array); // ['item1']
array[2] = 'item2'; // array is mutated
console.log(array); // ['item1', undefined, 'item2']
В приведенном выше коде переменная array
видоизменена. Как я могу выполнить ту же операцию, не изменяя массив?
Вы можете использовать Object.assign
:
Object.assign([], array, {2: newItem});
Вы можете просто создать новый массив как таковой:
const newItemArray = array.slice();
И затем установите значение для индекса, для которого вы хотите иметь значение.
newItemArray[position] = newItem
и вернуть это. Значения под промежуточными индексами будут иметь undefined
.
Или, очевидно, альтернативой будет:
Object.assign([], array, {<position_here>: newItem});
Ну, технически это не заменит, поскольку в индексе, который вы меняете, нет элемента.
Посмотрите, как это происходит в Clojure - языке, построенном вокруг канонических реализаций для неизменных структур данных.
(assoc [1] 2 3)
;; IndexOutOfBoundsException
Он не только терпит неудачу, но и падает. Эти структуры данных спроектированы так, чтобы быть максимально надежными, и когда вы сталкиваетесь с подобными ошибками, обычно это происходит не потому, что вы обнаружили крайний случай, а с большей вероятностью, что вы используете неправильную структуру данных.
Если у вас получаются разреженные массивы, рассмотрите возможность моделирования их с помощью объектов или карт.
let items = { 0: 1 };
{ ...items, 2: 3 };
// => { 0: 1, 2: 3 }
let items = new Map([ [0, 1] ]);
items(2, 3);
// => Map {0 => 1, 2 => 3}
Однако Map является принципиально изменчивой структурой данных, поэтому вам нужно заменить ее на неизменяемый вариант с такой библиотекой, как Immutable.js или Mori.
let items = Immutable.Map([ [0, 2] ]);
items.set(2, 3);
// => Immutable.Map {0 => 1, 2 => 3}
let items = mori.hashMap();
mori.assoc(items, 2, 3);
// => mori.hashMap {0 => 1, 2 => 3}
Конечно, для использования массивов JavaScript может быть вполне веская причина, так что здесь решение для хорошей меры.
function set(arr, index, val) {
if(index < arr.length) {
return [
...arr.slice(0, position),
val,
...arr.slice(position + 1)
];
} else {
return [
...arr,
...Array(index - arr.length),
val
];
}
}
function replaceAt(array, index, value) {
const ret = array.slice(0);
ret[index] = value;
return ret;
}
Смотрите JSPerf (благодаря @Bless)
Похожие сообщения:
Вот как я хотел бы это сделать:
function update(array, newItem, atIndex) {
return array.map((item, index) => index === atIndex ? newItem : item);
}
Как правило, операция с расширением массива создает для вас несколько временных массивов, но map
не работает, поэтому может быть быстрее. Вы также можете посмотреть это обсуждение в качестве ссылки
var list1 = ['a','b','c'];
var list2 = list1.slice();
list2.splice(2, 0, "beta", "gamma");
console.log(list1);
console.log(list2);
Другим способом может быть использование оператора распространения со слайсом как
let newVal = 33, position = 3;
let arr = [1,2,3,4,5];
let newArr = [...arr.slice(0,position - 1), newVal, ...arr.slice(position)];
console.log(newArr); //logs [1, 2, 33, 4, 5]
console.log(arr); //logs [1, 2, 3, 4, 5]