One of iteration Conway's Game of Life I would really like to get some feedback on how you would improve my code's style, readability, and efficiency. I failed an interview recently and was unable to get any feedback so I thought I would try asking here. I have cleaned up the code a bit already based on advice from friends but would like to have more critical eyes take a look.

My task was to write a function that takes a matrix of 1's and 0's and returns a resulting matrix after one iteration of Conway's Game of Life.

Note: Since the goal was to only return 1 iteration, I was told to not create cell objects and hold references to neighbors. There is not a need to set this up because we only want to run 1 iteration and doing so would waste space.

I would like advice on what you would change in terms of the function organization and how I decided to break down the problem. Specifically, it feels a little awkward how I have to pass the matrix all the way down to the isInBounds function. Is there a way to avoid this?

This or any other readability/optimization advice would be greatly appreciated.

function oneIterationConway(matrix) {
  const height = matrix.length;
  const width = matrix[0].length;
  const resultMatrix = [];

  for (let row = 0; row &lt; height; row++) {
    resultMatrix[row] = [];
    for (let col = 0; col &lt; width; col++) {
      const neighborCount = countNeighbors(row, col, matrix);
      let isAlive = 0;
      if (matrix[row][col]) {
        // current cell is alive
        isAlive = neighborCount &gt; 1 &amp;&amp; neighborCount &lt; 4 ? 1 : 0;
      } else {
        // current cell is dead
        isAlive = neighborCount === 3 ? 1 : 0;
      }

      resultMatrix[row][col] = isAlive;
    }
  }

  return resultMatrix;
}

function countNeighbors(row, col, matrix) {
  let neighbors = 0;

  for (let i = -1; i &lt;= 1; i++) {
    for (let j = -1; j &lt;= 1; j++) {
      if (i === 0 &amp;&amp; j === 0) continue;
      if (isInBounds(row + i, col + j, matrix) &amp;&amp; matrix[row + i][col + j]) {
        neighbors++;
      }
    }
  }

  return neighbors;
}

function isInBounds(row, col, matrix) {
  const height = matrix.length;
  const width = matrix[0].length; 
  return row &gt;= 0 &amp;&amp; col &gt;= 0 &amp;&amp; row &lt; height &amp;&amp; col &lt; width;
}


const testMatrix = [
  [1, 0, 0, 0, 0],
  [0, 1, 1, 0, 0],
  [0, 1, 0, 0, 0]
];

const result = oneIterationConway(testMatrix);

for (let row of result) {
  console.log(row);
}
There are a couple of ways of solving this:


Nest the other functions inside of oneIterationConway

function oneIterationConway(matrix) {
  const height = matrix.length;
  const width = matrix[0].length;
  function isInBounds(row, col, matrix) {
    return row &gt;= 0 &amp;&amp; col &gt;= 0 &amp;&amp; row &lt; height &amp;&amp; col &lt; width;
  }


this way the first definition of height is in scope (available) inside isInBounds. Hiding your 'private' functions is a good idea anyway.
Don't use isInBounds at all, take advantage of the fact that items out of bounds are undefined.

function countNeighbors(row, col, matrix) {
  let neighbors = -matrix[row, col];

  for (let i = -1; i &lt;= 1; i++) {
    for (let j = -1; j &lt;= 1; j++) {
      let value = matrix[row+i, col+j];
      if (value) neighbors++;
    }
  }
  return neighbors;
}

If I am in control of the design, something I often do is add a boundary around my matrix. Something like:

const testMatrix = [
  [0, 0, 0, 0, 0, 0, 0],
  [0, 1, 0, 0, 0, 0, 0],
  [0, 0, 1, 1, 0, 0, 0],
  [0, 0, 1, 0, 0, 0, 0],
  [0, 0, 0, 0, 0, 0, 0],
];



My loops would then be from 1 to size-2

The only other obvious thing I would be would be to factor out part of the main loop:

for (let row = 0; row &lt; height; row++) {
  resultMatrix[row] = [];
  for (let col = 0; col &lt; width; col++) {
    resultMatrix[row][col] = isAlive(row, col);
  }
} 

function isAlive(row, col) {
  const neighborCount = countNeighbors(row, col);
  if (matrix[row][col]) {
    // current cell is alive
    return neighborCount &gt; 1 &amp;&amp; neighborCount &lt; 4 ? 1 : 0;
  } else {
    // current cell is dead
    return neighborCount === 3 ? 1 : 0;
  }
}


btw, There is a nice feature in most environments. You can use: console.table(results)