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

How can I create a shallow copy of an array with a map like function?

如果我有一个 4x4 二维数组并想处理特定列,我可以使用映射获取它并将其设置回去,或者我可以获得它的浅表副本并直接处理原始数组值.

get/set方式

// 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 的获取),但它仍然可以完成工作。

  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

如果你想制作一个浅拷贝,你必须使用一个对象,而不是一个基元。

代码可能如下所示:

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

我不知道你的应用程序如何resource-sensitive,但这样你的内存消耗会增加一定程度(取决于应用程序)。

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

// get copy of arr with changed column
const cloneSquareAndChangeColumn = (square, colIndex, newValue) => {
  return square.map((row, col) => {
    return [...row.slice(0, colIndex), newValue, ...row.slice(colIndex + 1)];
  });
}
// test changing last column
console.log("cloned and changed arr: \n", cloneSquareAndChangeColumn(arr, 3, 1));

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

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

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 即可有效地旋转网格。您可以简单地制作 两个 网格 - 一个代表列,其他代表行。如果您首先从 "normal" 网格开始,用对象填充它然后转置它,那么您将有效地对同一数据有两个视图:

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");
}