0

If I have a 4x4 2d Array and want to work on a specific column, I can either get it using a map and set it back or I can get a shallow copy of it and work directly on the original array values.

The get/set way

// 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)
Now, how can I achieve the same thing with a shallow copy, i.e. without having to set the value back?


UPDATE

Here are my (ugly) getter/setter functions, probably very perfectible. It does not even properly deep copy in each case (e.g. the get with index > 11) but it still does the job.

  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
  }

FYI, the original problem is here: https://www.codewars.com/kata/4-by-4-skyscrapers

7
  • 4
    I don't get what the actual goal is here. You want to set one value on the grid? And somehow use a shallow copy for that? It seems a bit like an XY problem - can you describe what the underlying problem is? Commented Jun 19, 2019 at 11:26
  • I am trying to improve the performance of the skyscraper 4x4 kata from codeWars codewars.com/kata/4-by-4-skyscrapers with hope to tackle the 6x6 version. It requires (the way I do it) to get a line or column from every perspective, and update the possible values of each cell. I assume it would be a better practice to work directly on the array, and it is easier to do it from the line I am iterating on Commented Jun 19, 2019 at 11:29
  • why not use a setter function and switch the indices? any other solutions, like Proxy are slower than a simple for loop, even the array methods are slower. Commented Jun 19, 2019 at 11:34
  • 2
    You cannot make a shallow copy of primitive values. If you need to work with columns as if they are rows (single array) and you don't want to do a matrix transposition all the time, then perhaps you can create two 4x4 grids - one contains the rows as each member, the other is effectively the transposed first one. You can add the same objects to both, so rows[0][2] will correspond to columns[2][0] and thus you can grab a column or a row as needed then update the object and both will change. Commented Jun 19, 2019 at 11:36
  • If you consider it an XY problem, I can delete the question. However, if the question still makes sense, since @VLAZ suggestion could be an answer, you are welcome to write it as such :) Commented Jun 19, 2019 at 11:40

3 Answers 3

2

You cannot make a "shallow copy" of primitive values - when you make arr.map(tile => tile[3]) you are making a new array with new values, so changing one doesn't change the other.

However, you can make an array of objects, as the values of objects are their references

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

If you need to make frequent changes based on columns, you don't need to do map(row => row[columnNumber]) every time or even matrix transposition to effectively rotate the grid. You can simply make two grids - one representing columns, the other rows. If you start with the "normal" grid first, fill it with objects and then transpose it, then you will have effectively two views over the same data:

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

Sign up to request clarification or add additional context in comments.

Comments

1

If you want to make a shallow copy you have to use an object, and not a primitive.

The code could look like this then:

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

I don't know how resource-sensitive your app, but this way your memory consumption will go up by some degree (depends on the app).

Comments

0

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.