Cmaxster
Cmaxster

Reputation: 1195

Trying to Populate a Grid of Coordinates into a Multidimensional Array Using Nested Array Map Methods

I'm trying to populate a grid of coordinates into a multidimensional array using ES6 nested map methods using this code:

var gridSize = 4;
          var regionArray = [];

          for (var i = 0; i < gridSize; i++) regionArray.push(new Array(gridSize).fill({start:{x:0, y:0}, end:{x:0, y:0}}));  
          
          var regionWidth = canvas.width/gridSize;
          var regionHeight = canvas.height/gridSize;

          console.log(`regionWidth:${regionWidth} regionHeight:${regionHeight}`)

          const hitRegions = regionArray.map((regionRow, i) => {
            regionRow.forEach((region, j) => {
              region.start.x = j*regionWidth;
              region.end.x = j*regionWidth+regionWidth;
              region.start.y = (i*regionHeight)+regionHeight;
              region.end.y = (i*regionHeight)+regionHeight;
              console.log(`>> region i:${i} j:${j} start.x:${region.start.x} end.x:${region.end.x} start.y:${region.start.y} end.y: ${region.end.y}`)
            })
            console.log('>> regionRow : ',regionRow)
            return regionRow;
          })

          console.log(hitRegions);

The Issue I'm having is that the "regionRow" console.log is returning an array with all of the x values the same for each index.

Meanwhile the values in the ">> region" log above it is logging the correct values.

I'm trying to understand why this is happening and how to fix it..

Upvotes: 0

Views: 245

Answers (2)

Gianluca Fuoco
Gianluca Fuoco

Reputation: 303

The method Array.prototype.fill() copies the value to each element.

When you put an object, it just copies the reference over. Meaning that if you update one element, all elements will be changed.

You can simply use a nested for loop to get what you want:

for (let i = 0; i < gridSize; i++) {
  const arr = new Array(gridSize);
  for (let i = 0; i < gridSize; i++) {
    arr.push({ start: { x: 1, y: 1 }, end: { x: 2, y: 2 } });
  }
  regionArray.push(arr);
}

Upvotes: 0

danielm2402
danielm2402

Reputation: 788

In javascript everything is passed by reference, for this reason there are methods that do not directly alter the reference (Map, Filter, Reduce) in short, they return a copy of your array and it is modified on that copy.

This also happens with objects! Generally the spread operator is used to make a copy of an object and be able to modify the properties without obtaining side effects, like this:

const obj = {...oldObject}

The problem is when there are several levels in the object to mutate, Using the spread operator does not have a deep copy, therefore if we modify a property in a second level, we will be modifying the main object.

There are several options to fix this, and it will depend on how you are organizing your app:

  1. You can use lodash (I think it's deprecated) with its cloneDeep method
  2. You can use a library for immutability like ImmerJS
  3. You can create your own method to clone

To quickly solve the problem you have, I did it using JSON.parse and JSON.stringify. This way you can have a copy and modify the deep objects.

The code would look like this:

const hitRegions = regionArray.map((regionRow, i) => {
  const newRegionRow = regionRow.map((region, j) => {
    var newObject = JSON.parse(JSON.stringify(region))
    newObject.start.x = j * regionWidth;
    newObject.end.x = j * regionWidth + regionWidth;
    newObject.start.y = (i * regionHeight) + regionHeight;
    newObject.end.y = (i * regionHeight) + regionHeight;
    
    return newObject
  })
  return newRegionRow
})

console.log(hitRegions);

This solution is a bit dangerous if your objects have functions or a value that can't be directly converted to json, so be careful.

Upvotes: 1

Related Questions