Rob Bailey
Rob Bailey

Reputation: 981

How do I get TypeScript Array.map() to return the same as VanillaJS?

Within my Angular6 App, I am making Conway's Game of Life. I am trying to generate an n x m two dimensional array of class instances. In vanillaJS, I got this to work as:

generateInitialState(bias) {
    return [...Array(this.rows)]
        .map((a, i) => [...Array(this.columns)]
            .map((b, j) => new Cell(j, i, (Math.round(Math.random() * 1000) < (1000 * bias)) ? 'alive' : 'dead')));
}

This generates an Array of length this.rows containing Arrays populated by (n = this.columns) instances of the Cell class. E.g. when this.rows = this.columns = 4 (from the console):

[ [ Cell { x: 0, y: 0, state: 'alive' },
        Cell { x: 1, y: 0, state: 'dead' },
        Cell { x: 2, y: 0, state: 'alive' },
        Cell { x: 3, y: 0, state: 'dead' } ],
      [ Cell { x: 0, y: 1, state: 'alive' },
        Cell { x: 1, y: 1, state: 'alive' },
        Cell { x: 2, y: 1, state: 'dead' },
        Cell { x: 3, y: 1, state: 'dead' } ],
      [ Cell { x: 0, y: 2, state: 'alive' },
        Cell { x: 1, y: 2, state: 'alive' },
        Cell { x: 2, y: 2, state: 'alive' },
        Cell { x: 3, y: 2, state: 'dead' } ],
      [ Cell { x: 0, y: 3, state: 'dead' },
        Cell { x: 1, y: 3, state: 'alive' },
        Cell { x: 2, y: 3, state: 'alive' },
        Cell { x: 3, y: 3, state: 'alive' } ] ] 

In vanillaJS this works fine and generates the Array as needed. However, the above code in Typescript only returns an empty array of length this.rows. TypeScript appears to compile it down to:

function generateInitialState(bias) {
var _this = this;
return Array(this.rows).slice().map(function (a, i) { return Array(_this.columns).slice().map(function (b, j) { return new Cell(j, i, (Math.round(Math.random() * 1000) < (1000 * bias)) ? 'alive' : 'dead'); }); });
}

How would I get it to work in TypeScript?

Full Code

class Game {
  constructor(columns, rows, randomBias){
    this.columns = columns; 
    this.rows = rows;
    this.randomBias = randomBias;
    this.cells = this.generateInitialState(this.randomBias);
  }
  /* Content omitted for brevity */
  generateInitialState(bias) {
    return [...Array(this.rows)]
      .map((a, i) => [...Array(this.columns)]
        .map((b, j) => new Cell(j, i, (Math.round(Math.random() * 1000) < (1000 * bias)) ? 'alive' : 'dead')));
  }
}
class Cell{
  constructor(x, y, state){
    this.x = x;
    this.y = y;
    this.state = state;
  }
}
let a = new Game(4, 4, 0.5);
console.log(a.cells);

Upvotes: 1

Views: 53

Answers (1)

dfsq
dfsq

Reputation: 193261

The problematic place is how you initialize array of specified size. When you do this:

[...Array(this.rows)]

it gets compiled to Array(this.rows).slice() which doesn't produce any values, because the array is filled with "holes", which is not the same as array filled with undefined values, which you get in original (uncompiled) version. Holes are not processed by map.

Try Array.from({ length: this.rows }) instead:

function generateInitialState(bias) {
  return Array.from({ length: this.rows })
    .map((a, i) => Array.from({ length: this.columns })
      .map((b, j) => new Cell(j, i, (Math.round(Math.random() * 1000) < (1000 * bias)) ? 'alive' : 'dead')));
}

Upvotes: 2

Related Questions