替换数组中特定位置的元素而不改变它
Replace element at specific position in an array without mutating it
如何在不改变数组的情况下完成以下操作:
let array = ['item1'];
console.log(array); // ['item1']
array[2] = 'item2'; // array is mutated
console.log(array); // ['item1', undefined, 'item2']
在上面的代码中,array
变量发生了变化。如何在不改变数组的情况下执行相同的操作?
您可以像这样简单地设置一个新数组:
const newItemArray = array.slice();
然后为您希望为其赋值的索引设置值。
newItemArray[position] = newItem
和return那个。中间索引下的值将具有 undefined
.
或者明显的替代方案是:
Object.assign([], array, {<position_here>: newItem});
var list1 = ['a','b','c'];
var list2 = list1.slice();
list2.splice(2, 0, "beta", "gamma");
console.log(list1);
console.log(list2);
这是你想要的吗?
嗯,从技术上讲,这不会替换,因为您要更改的索引中没有项目。
看看 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 or 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
];
}
}
您可以使用 Object.assign
:
Object.assign([], array, {2: newItem});
这是我想要的方式:
function update(array, newItem, atIndex) {
return array.map((item, index) => index === atIndex ? newItem : item);
}
通常,数组展开操作会为您生成一些临时数组,但 map
不会,因此它可以更快。也可以看看this discussion作为参考
快速方式
function replaceAt(array, index, value) {
const ret = array.slice(0);
ret[index] = value;
return ret;
}
相关帖子:
- Javascript fastest way to duplicate an Array - slice vs for loop
- https://github.com/lodash/lodash/issues/2053#issuecomment-188776090
另一种方法是将扩展运算符与切片一起使用
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]
这个怎么样:
const newArray = [...array]; // make a copy of the original array
newArray[2] = 'item2'; // mutate the copy
我发现意图比这个单行更清楚:
const newArray = Object.assign([...array], {2: 'item2'});
有一个新的 tc39 proposal,它向 Array
添加了一个 with
方法,returns 数组的副本并且不修改原始数组:
Array.prototype.with(index, value) -> Array
提案示例:
const correctionNeeded = [1, 1, 3];
correctionNeeded.with(1, 2); // => [1, 2, 3]
correctionNeeded; // => [1, 1, 3]
(请注意,如果 with
的第一个参数超出数组范围,将抛出 RangeError
,因此问题中的具体示例将不起作用)
由于它目前处于第 3 阶段,它可能很快就会在浏览器引擎中实现,但同时可以使用 polyfill here or in core-js
。
如何在不改变数组的情况下完成以下操作:
let array = ['item1'];
console.log(array); // ['item1']
array[2] = 'item2'; // array is mutated
console.log(array); // ['item1', undefined, 'item2']
在上面的代码中,array
变量发生了变化。如何在不改变数组的情况下执行相同的操作?
您可以像这样简单地设置一个新数组:
const newItemArray = array.slice();
然后为您希望为其赋值的索引设置值。
newItemArray[position] = newItem
和return那个。中间索引下的值将具有 undefined
.
或者明显的替代方案是:
Object.assign([], array, {<position_here>: newItem});
var list1 = ['a','b','c'];
var list2 = list1.slice();
list2.splice(2, 0, "beta", "gamma");
console.log(list1);
console.log(list2);
这是你想要的吗?
嗯,从技术上讲,这不会替换,因为您要更改的索引中没有项目。
看看 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 or 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
];
}
}
您可以使用 Object.assign
:
Object.assign([], array, {2: newItem});
这是我想要的方式:
function update(array, newItem, atIndex) {
return array.map((item, index) => index === atIndex ? newItem : item);
}
通常,数组展开操作会为您生成一些临时数组,但 map
不会,因此它可以更快。也可以看看this discussion作为参考
快速方式
function replaceAt(array, index, value) {
const ret = array.slice(0);
ret[index] = value;
return ret;
}
相关帖子:
- Javascript fastest way to duplicate an Array - slice vs for loop
- https://github.com/lodash/lodash/issues/2053#issuecomment-188776090
另一种方法是将扩展运算符与切片一起使用
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]
这个怎么样:
const newArray = [...array]; // make a copy of the original array
newArray[2] = 'item2'; // mutate the copy
我发现意图比这个单行更清楚:
const newArray = Object.assign([...array], {2: 'item2'});
有一个新的 tc39 proposal,它向 Array
添加了一个 with
方法,returns 数组的副本并且不修改原始数组:
Array.prototype.with(index, value) -> Array
提案示例:
const correctionNeeded = [1, 1, 3];
correctionNeeded.with(1, 2); // => [1, 2, 3]
correctionNeeded; // => [1, 1, 3]
(请注意,如果 with
的第一个参数超出数组范围,将抛出 RangeError
,因此问题中的具体示例将不起作用)
由于它目前处于第 3 阶段,它可能很快就会在浏览器引擎中实现,但同时可以使用 polyfill here or in core-js
。