1

I'm making a puzzle game that relies on elements being placed in grids and evaluating the values at certain x,y locations. My data is stored in 2d arrays, so regular maps don't work that well. As a result, I made this helper function to map over every single cell in a 2d array.

// makes transformations on the cell level in a 2d array
export const twoDMap = (twoDimensionalArray, mapFunction) => {
  return twoDimensionalArray.map((row, y) => {
    return row.map(mapFunction);
  })
}

This works fine most of the time, except when I need to use the x,y indexes of a cell in my 2d map. In the test case below, I can't seem to access the y value of the cells. (I got rid of the ES6 syntax so it'll run in the console)

var testArray = [[true, "random-string", 3],
                 ["potato", false, 4],
                 [null, 4, 5]];

function twoDMap(twoDimensionalArray, mapFunction) {
 return twoDimensionalArray.map(function (row, y) {
   return row.map(mapFunction);
 })
}

function logCells(cells)  {
  twoDMap(cells ,function(cell, x) {
    console.log(cell, "location: ", x);
    // console.log(cell, "location: ", x, y); //throws an error
  })
}

logCells(testArray);

I'm pretty sure I need to make some kind of change to twoDMap, and it's probably quite minor, but I've been on this problem for hours and still can't get it to work. When I write it out manually (using regular nested maps instead of twoDMap ), it works fine, but it's overly verbose and I'd like to avoid it considering that it's a common pattern in this project. This is the common suggestion that I keep finding whenever I google this problem, but it's not a viable solution given how much nesting I need to do for this project.

Essentially, I'm looking for a way to modify twoDMap or the callbacks that are sent into it, so that my callbacks can access the x and y indexes of the 2d array sent to twoDMap.

4
  • row.map, you could alter this to accept the y as another param, and then pass this to the callback so that mapFunction callback get's the Y,.. But I must say all this looks very convoluted to access a 2d array. Commented Oct 28, 2017 at 22:32
  • @Keith how would you alter row.map? I've tried different things and nothing's given me the result I was looking for so far. In some cases it's super easy to access the arrays ( array[y][x] ), but in this case the 2d arrays are of variable shapes and sizes (flattening them won't yield consistent results). Commented Oct 28, 2017 at 22:48
  • Are you able to post a working snippet? Commented Oct 28, 2017 at 23:00
  • @Keith I added a self-contained snippet that can be run in the console. Commented Oct 29, 2017 at 15:53

2 Answers 2

1

The issue is that the 'y' is out of scope to the current function, so it can't 'see' the value. For this reason you need to work out how to make it available to the second function. The two approaches I see:

  • Make second function's map an inner operation, within twoDimensionalArray.map(function (row, y)
  • Find a way to pass 'y' to the second function

For the first approach:

var testArray = [[true, "random-string", 3],
                 ["potato", false, 4],
                 [null, 4, 5]];

function twoDMap(twoDimensionalArray) {
 return twoDimensionalArray.map(function (row, y) {
   return row.map(function (row, x) {
     console.log(cell, "location: ", x, y);
   }
 })
}    

twoDMap(testArray);

Below takes the second approach, using the bind() function:

var testArray = [[true, "random-string", 3],
                 ["potato", false, 4],
                 [null, 4, 5]];

function twoDMap(twoDimensionalArray, mapFunction) {
 return twoDimensionalArray.map(function (row, y) {
   return row.map(mapFunction.bind(this, y);
 })
}

function logCells(cells)  {
  twoDMap(cells , function(y, cell, x) {    
    console.log(cell, "location: ", x, y);
  })
}

logCells(testArray);

When binding a values to a function always remember the first parameter should correspond to how you wish to define this and then the subsequent parameters are provided as initial parameters to the function you are calling.

I should add that sometimes using the traditional for loops makes for more manageable code, and sometimes benefit from better performance. An alternative, using for loops:

var testArray = [[true, "random-string", 3],
                 ["potato", false, 4],
                 [null, 4, 5]];

function twoDMap(twoDimensionalArray) {
  for (var y=0; y<twoDimensionalArray.length; y++) {
     for (var x=0; x<twoDimensionalArray[y].length; x++) {
        console.log(twoDimensionalArray[y][x], "location: ", x, y);
     }
  }
}

twoDMap(testArray);
Sign up to request clarification or add additional context in comments.

Comments

1

Turns out, re-implementing the inner map as a for loop and then manually passing the coordinates to the transforming callback does the trick. The callback in array.map has to follow a set format for its arguments ( callback(element, index, array)) but by having the callback act alone in a for loop, that restriction is lifted.

var testArray = [[true, "random-string", 3],
                 ["potato", false, 4],
                 [null, 4, 5]];

function twoDMap(twoDimensionalArray, transform) {
 return twoDimensionalArray.map(function (row, y) {
   var mapped = [];
   for (var x = 0; x < row.length; x++)
     mapped.push(transform(row[x], x, y));
   return mapped;
 })
}

twoDMap(testArray, function(cell, x, y) {
  console.log(cell, x, y);
})

if someone knows why the first attempt with the nested map didn't work, I'd be very curious to learn about that. I still don't know why it didn't have access to the scope that contained the y...

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.