如何在不创建“依赖元素”的情况下复制js数组中的元素?

时间:2019-05-27 17:36:45

标签: javascript arrays

我正在尝试修改数组中的单个元素,该数组之前的元素重复了 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也被修改了,尽管这不是故意的。如何避免这个问题?

3 个答案:

答案 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 }));