如何使用类似地图的函数创建数组的浅表副本?

时间:2019-06-19 11:20:23

标签: javascript shallow-copy

如果我有一个4x4 2d数组并想处理特定的列,则可以使用映射将其获取并重新设置,也可以获取其浅表副本并直接处理原始数组值。 / p>

获取/设置方式

// create the original array
let arr = [...Array(4)].map(e => Array(4).fill(0))
console.log(arr)

// get a specific column, e.g. the 3d
let myColumn = arr.map(tile => tile[3])

// change it
myColumn[2] = 1

// set it back
arr.map((line, i) => line[3] = myColumn[i])

console.log("modified array", arr)
现在,如何通过浅表复制(即不必重新设置值)实现相同的目的?


更新

这是我的(难看的)getter / setter函数,可能非常完善。在每种情况下,它甚至都无法正确进行深度复制(例如,使用index > 11进行get复制),但仍然可以完成工作。

  const getLine = (index) => {
    if (index < 4) return field.map(fieldLine => fieldLine[index])
    if (index > 3 && index < 8) return field[index - 4].slice().reverse()
    if (index > 7 && index < 12) return field.map(fieldLine => fieldLine[Math.abs(index - 11)]).slice().reverse()
    if (index > 11) return field.slice()[Math.abs(index - 15)]
  }
  const setLine = (index, line) => {
    if (index < 4) field.map((fieldLine, i) => fieldLine[index] = line[i])
    if (index > 3 && index < 8) field[index - 4] = line.reverse()
    if (index > 7 && index < 12) field.slice().reverse().map((fieldLine, i) => fieldLine[Math.abs(index - 11)] = line[i])
    if (index > 11) field[Math.abs(index - 15)] = line
  }

仅供参考,原来的问题在这里:https://www.codewars.com/kata/4-by-4-skyscrapers

4 个答案:

答案 0 :(得分:2)

cannot make a "shallow copy" of primitive values-当您制作arr.map(tile => tile[3])时,您正在使用 new 值创建一个新数组,因此更改一个不会更改另一个。

但是,您可以创建 objects 的数组,因为对象的值是它们的引用

let grid = [
  [{value: 1}, {value: 2}],
  [{value: 3}, {value: 4}],
];

//take a column
let col2 = grid.map(row => row[1]);

//change one value
col2[1].value = 42;

//the original changed
console.log(grid);

如果您需要基于列进行频繁的更改,则不需要每次都map(row => row[columnNumber]),甚至不需要matrix transposition来有效地旋转网格。您可以简单地制作两个 网格-一个代表列,其他代表行。如果首先从“普通”网格开始,将其填充对象,然后对其进行转置,那么您将对同一数据有效地拥有两个视图:

let rows = [
  [ {cell: "a1"}, {cell: "a2"}, {cell: "a3"}, {cell: "a4"} ],
  [ {cell: "b1"}, {cell: "b2"}, {cell: "b3"}, {cell: "b4"} ],
  [ {cell: "c1"}, {cell: "c2"}, {cell: "c3"}, {cell: "c4"} ],
  [ {cell: "d1"}, {cell: "d2"}, {cell: "d3"}, {cell: "d4"} ]
];

//transpose
let columns = rows[0].map((col, i) => rows.map(row => row[i]));

//show 
console.log("rows:");
console.log(format(rows));
console.log("----");
console.log("columns:");
console.log(format(columns));
console.log("----");

//take a column
let col2 = columns[1];

//update a value
col2[2].cell = "XX";

//show again
console.log("after the change");
console.log("rows:");
console.log(format(rows));
console.log("----");
console.log("columns:");
console.log(format(columns));
console.log("----");

//helper function to display the grids more compactly
function format(arr) {
   return arr
     .map(arr => arr.map(({cell}) => cell).join())
     .join("\n");
}

答案 1 :(得分:1)

如果要进行浅表复制,则必须使用对象而不是原始对象。

然后代码可能像这样:

// filling the array:
/*...*/.fill({value: 0})
// changing the value:
myColumn[2].value = 1

我不知道您的应用程序对资源的敏感程度,但是这样您的内存消耗将在某种程度上增加(取决于应用程序)。

答案 2 :(得分:1)

1。传播算子(浅副本)

numbers = [1, 2, 3];numbersCopy = [...numbers];

注意:这不能安全地复制多维数组。数组/对象值是按引用而不是按值复制的。

这很好

numbersCopy.push(4);
console.log(numbers, numbersCopy);
// [1, 2, 3] and [1, 2, 3, 4]// numbers is left alone

这不好

nestedNumbers = [[1], [2]];numbersCopy = [...nestedNumbers];

2。 Good Old for()循环(浅拷贝)

考虑到功能性编程在我们圈子中的流行程度,我认为这种方法最受欢迎。 纯粹或不纯,声明式或命令式,就可以完成工作!

numbers = [1, 2, 3];numbersCopy = [];
for (i = 0; i < numbers.length; i++) {  
   numbersCopy[i] = numbers[i];
}

注意:这不能安全地复制多维数组。由于使用的是=运算符,它将通过引用而不是通过值分配对象/数组。

这很好

numbersCopy.push(4);
console.log(numbers, numbersCopy);
// [1, 2, 3] and [1, 2, 3, 4]// numbers is left alone

这不好

nestedNumbers = [[1], [2]];numbersCopy = [];
for (i = 0; i < nestedNumbers.length; i++) {  
   numbersCopy[i] = nestedNumbers[i];
}
numbersCopy[0].push(300);
console.log(nestedNumbers, numbersCopy);
// [[1, 300], [2]]// [[1, 300], [2]]// They've both been changed because they share references

3。 Array.map(浅副本)

要将数字列表加倍,请使用带有double函数的map。

numbers = [1, 2, 3];
double = (x) => x * 2;
numbers.map(double);

4。 Array.filter(浅拷贝)

此函数返回一个数组,就像map一样,但是不能保证长度相同。

如果要过滤偶数怎么办?

[1, 2, 3].filter((x) => x % 2 === 0)// [2]

输入数组的长度为3,但结果为1。

但是,如果您的过滤器的谓词始终返回true,那么您将得到一个重复项!

numbers = [1, 2, 3];numbersCopy = numbers.filter(() => true);

每个元素都通过了测试,因此可以返回。

注意:这还会通过引用而不是值来分配对象/数组。

5。 Array.reduce(浅副本)

使用reduce克隆数组几乎让我感到难过,因为它的功能要强大得多。但是,我们开始...

numbers = [1, 2, 3];
numbersCopy = numbers.reduce((newArray, element) => {  
newArray.push(element);
  return newArray;
}, []);

reduce在遍历列表时转换初始值。

此处的初始值是一个空数组,我们将使用每个元素填充它。该数组必须从函数中返回,以便在下一次迭代中使用。

注意:这还会通过引用而不是值来分配对象/数组。

答案 3 :(得分:0)

T