Hollow square

Medium TimeO(n²) SpaceO(n²)

Given a side length n, return an n × n square whose border is stars and whose inside is spaces, as an array of n strings. A position holds a * when it is on the first or last row, or in the first or last column, and a space otherwise. Every row is exactly n characters wide.

Examples

Example 1

Input
n = 5
Output
*****
*   *
*   *
*   *
*****
["*****","* *","* *","* *","*****"]
16 of the 25 positions sit on a border; the 9 in the middle are spaces.

Example 2

Input
n = 2
Output
**
**
["**","**"]
At a side of 2 every position is on a border, so there is nothing left to hollow out.

The Code

function hollowSquare(n) {
  const rows = [];
  for (let row = 1; row <= n; row++) {
    let line = "";
    for (let col = 1; col <= n; col++) {
      const onEdge = row === 1 || row === n || col === 1 || col === n;
      line += onEdge ? "*" : " ";
    }
    rows.push(line);
  }
  return rows;
}
hollowSquare(5);
Done

The first 15 calls, of 37. This one does not fit on a page.

Step through hollowSquare(5) call by call

Explanation

Every row is the same length, so nothing here is decided by counting. Each position is asked where it sits and answers on its own: the shape comes out of the test, not the bounds. Each space is drawn as · in the rows below.

  1. 1

    row is 1, so the first test in onEdge — row === 1 — is already true and the other three never have to be asked. Every one of the five columns takes a *.

    *****
    0

    row=1onEdge=true for all col

    rows.push("*****")

  2. 2

    row is 2, which is neither 1 nor n, so onEdge now rests entirely on the column: true at col 1 and col 5, false at 2, 3 and 4. The false ones append a space rather than being skipped.

    *****
    0
    *···*
    1

    row=2onEdge=true at col 1, 5

    rows.push("*···*")

  3. 3

    row is 3, and nothing about the row enters the test any more, so it produces the same five characters as the row before it.

    *****
    0
    *···*
    1
    *···*
    2

    row=3onEdge=true at col 1, 5

    rows.push("*···*")

  4. 4

    row is 4 — the last of the rows between the two solid ones.

    *****
    0
    *···*
    1
    *···*
    2
    *···*
    3

    row=4onEdge=true at col 1, 5

    rows.push("*···*")

  5. 5

    row is 5, which is n, so row === n makes onEdge true across the whole row and the square closes. The inner loop ran n times on every row, whatever the answers were.

    *****
    0
    *···*
    1
    *···*
    2
    *···*
    3
    *****
    4

    row=5onEdge=true for all col

    return rows → 5 rows