我正在尝试修改数组中的单个元素,该数组之前的元素重复了 n 次。为了执行数组复制,我仅依赖于this帖子中的自定义函数duplicateElements(array, times)
(请参见@Bamieh答案)。如下例所示,问题是我不能在不修改其他元素的情况下修改数组中的单个元素:
function duplicateElements(array, times) {
return array.reduce((res, current) => {
return res.concat(Array(times).fill(current));
}, []);
}
var myvar = duplicateElements([{ a: 1 }, { a: 2 }], 2);
myvar[0].a = 3;
console.log(myvar);
// (4) [{…}, {…}, {…}, {…}]
// 0: {a: 3}
// 1: {a: 3}
// 2: {a: 2}
// 3: {a: 2}
// length: 4
您可以看到,myvar[1].a
也被修改了,尽管这不是故意的。如何避免这个问题?
答案 0 :(得分:1)
问题在于您正在将引用传递给Array(times).fill(current)中的原始对象。
在这种情况下,第一个{a:2}的两个副本是原始副本的相同副本(它们引用内存中的相同空间),因此,如果您更改一个副本,则两个副本将在引用副本时更改。内存中的相同对象。
您必须执行深克隆功能,或者可能将对象传播到新对象中。您可以更改原始函数以使用如下对象和基元:
function duplicateElements(elementsArray, times) {
//Make a new placeholder array
var newArray = [];
//Loop the array of elements you want to duplicate
for (let index = 0; index < elementsArray.length; index++) {
//Current element of the array of element
var currentElement = elementsArray[index];
//Current type of the element to check if it is an object or not
var currentType = typeof currentElement
//Loop over the times you want to copy the element
for (let index = 0; index < times; index++) {
//If the new element is not an object
if (currentType !== "object" && currentType){
//append the element
newArray.push(currentElement)
//if it is an Object
} else if (currentType === "object" && currentType){
//append an spreaded new Object https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax
newArray.push({...currentElement})
}
}
}
return newArray;
}
这不是执行此操作的最佳方法,但是我认为也许您是javascript新手,最好在使用更多Array功能之前学习旧的循环方法(如Jonas Wilms的回答,那也是一个很好的答案)。
我会推荐javascript.info和雄辩的javascript,以进一步了解该语言
答案 1 :(得分:1)
Array.fill文档中指定的主要原因是,在处理对象时,它将通过引用进行复制:
当填充通过一个对象时,它将复制引用并填充 对该对象的引用的数组。
使用lodash(和_.cloneDeep
)是这样的一行:
let dubFn = (arr, t=1) =>
_.concat(arr, _.flatMap(_.times(t, 0), x => _.cloneDeep(arr)))
let r1 = dubFn([{a:1},{b:3}]) // no parameter would mean just 1 dub
let r2 = dubFn([{a:1},{b:3},5,[1]], 2) // 2 dublicates
r1[0].a = 3
r2[0].a = 3
console.log(r1)
console.log(r2)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
请注意,这现在适用于数组/对象和基元。
这个想法是使用_.concat
返回输入数组的新串联版本,并结合一些函数,这些函数最终返回一个克隆对象数组。在这种情况下,我们使用_.times
返回一个数组t
,然后为每个元素替换为数组deep clone
。需要_.flatMap
来平整最终结果,因为我们在_.times
调用之后最终得到了数组数组。
使用ES6,您可以执行以下操作:
let dubElements = (arr, t) =>
[...arr, ...new Array(t).fill().flatMap(x => arr.map(y => ({...y})))]
let r1 = dubElements([{a:1},{b:3}])
let r2 = dubElements([{a:1},{b:3}],2)
r1[0].a = 3
r2[0].a = 3
console.log(r1)
console.log(r2)
在这里我们通过散布运算符连接数组,并使用new Array(t)
创建新的重复数组,并确保在这种情况下我们用fill
undefined
将其{{1} }}结果(我们再次通过传播算子通过flatMap
进行映射。
请注意,此方法有效clone
。如果要使其更通用,则必须在上一个map函数等中进行更多扩展。
如果要保留元素的顺序,可以执行以下操作:
for your use case specifically
答案 2 :(得分:0)
替换
Array(times).fill(current)
使用以下命令将多次向数组添加一个对current
的引用:
Array.from({ length: times }, () => ({...current }))
将浅克隆current
。请注意,尽管如此,代码将仅适用于对象,不适用于基元。
我愿意:
const duplicateElements = (array, length) =>
array.flatMap(current => Array.from({ length }, () => ({ ...current }));