Как я могу создать поверхностную копию массива с функцией, подобной карте? - PullRequest
0 голосов
/ 19 июня 2019

Если у меня есть 2x Array 4x4 и я хочу работать с конкретным столбцом, я могу либо получить его с помощью карты и установить обратно, либо я могу получить его поверхностную копию и работать непосредственно с исходными значениями массива.

Способ получения / установки

// 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)
Теперь, как я могу добиться того же с мелкой копией, то есть без необходимости сбрасывать значение обратно?

ОБНОВЛЕНИЕ

Вот мои (некрасивые) функции получения / установки, вероятно, очень совершенные. Он даже не делает должного глубокого копирования в каждом случае (например, получает с 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

Ответы [ 4 ]

2 голосов
/ 19 июня 2019

Вы не можете сделать «мелкую копию» примитивных значений - когда вы делаете 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]) или даже преобразование матрицы вращать сетку.Вы можете просто создать две сетки - одна представляет столбцы, а другие строки.Если вы сначала начнете с «нормальной» сетки, заполните ее объектами, а затем транспонируете ее, то у вас фактически будет два вида на одни и те же данные:

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 голос
/ 19 июня 2019

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.Старый добрый for () Loop (Shallow copy)

Я полагаю, что этот подход наименее популярен, учитывая, как модное функциональное программирование стало в наших кругах.Чистый или нечистый, декларативный или императивный, он выполняет свою работу!

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 (мелкая копия)

Чтобы удвоить список чисел, используйте карту с двойной функцией.

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

4.Array.filter (Shallow copy)

Эта функция возвращает массив, как и map, но она не гарантированно имеет одинаковую длину.

Что делать, если вы фильтруете дажечисла?

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

Длина входного массива была 3, но результирующая длина равна 1.

Если предикат вашего фильтра всегда возвращает true, однако, вы получите дубликат!

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

Каждый элемент проходит тест, поэтому он возвращается.

Примечание: Это также назначает объекты / массивы по ссылке, а не по значению.

5.Array.reduce (Shallow copy)

Я почти чувствую себя плохо, используя Reduce для клонирования массива, потому что он намного мощнее этого.Но здесь мы идем ...

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

при уменьшении преобразует начальное значение при циклическом просмотре списка.

Здесь начальное значение является пустым массивом, и мы заполняем его каждым элементомкак мы идем.Этот массив должен быть возвращен из функции, которая будет использоваться в следующей итерации.

Примечание: Это также назначает объекты / массивы по ссылке, а не по значению.

1 голос
/ 19 июня 2019

Если вы хотите сделать мелкую копию, вы должны использовать объект, а не примитив.

Код может выглядеть так:

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

Я не знаю, насколько чувствительно к ресурсам ваше приложение, но при этом потребление памяти будет увеличиваться до некоторой степени (зависит от приложения).

0 голосов
/ 19 июня 2019

// 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));
...