PhilVarg
PhilVarg

Reputation: 4820

Update multiple entries in an ImmutableJS List

Specifically, I have an ImmutableJS List of Maps, and I want to conditionally update records to the same value (which may include just updating all of them). The use case is revealing mines in minesweeper, when someone loses the game. this is accomplished by setting the isVisible tile to true, if isMine is also true (or just make every tile visible, regardless of isMine)

so my JS schema is like this (where arrays are lists and objects are maps):

game = {
  isGameOver: false,
  tiles: [
    {
      isMine: boolean,
      isRevealed: boolean
    },
    ...
  ]
}

and so what I'm trying to do is, starting with game, set isRevealed to true for every tile where isMine is true.

this is what i came up with, but it looks so awful I have to hope there's another way

function revealAll(game){
  let revealedTiles;

  revealedTiles = game.get('tiles').map(tile => {
    if (tile.get('isMine')) {
      tile = tile.set('isRevealed', true);
    }
    return tile;
  });

  return game.set('isGameOver', true).set('tiles', revealedTiles);
}

this successfully ends the game (sets isGameOver to true) and reveals all tiles that are mines (sets isRevealed to true for every tile with isMine equal to true), but I can tell just looking at it that its both inefficient and messy. Is there a built in way to accomplish what i'm doing here?

Upvotes: 2

Views: 1007

Answers (2)

Brian Neisler
Brian Neisler

Reputation: 913

Here's a functional programming approach using mudash. The benefit of this approach is that mudash will handle both ImmutableJS data types as well as standard JS (or a mix of both). So your function can be used no matter the format.

import _ from 'mudash'
import fp from 'mudash/fp'

const revealAll = _.compose(
  fp.set('isGameOver', true),
  fp.update('tiles', fp.map((tile) =>
    _.update(tile, 'isRevealed', (isRevealed) => _.get(tile, 'isMine') || isRevealed)))
)

const gameMutable = {
  isGameOver: false,
  tiles: [
    {
      isMine: false,
      isRevealed: false
    },
    {
      isMine: true,
      isRevealed: false
    },
    {
      isMine: false,
      isRevealed: true
    },
    {
      isMine: true,
      isRevealed: true
    }
  ]
}
const gameImmutable = _.immutable(gameMutable)

console.log(revealAll(gameMutable))
console.log(revealAll(gameImmutable))

Upvotes: 0

Tomas Kulich
Tomas Kulich

Reputation: 15638

From my point of view, the code is quite fine :) There are however few tricks that may make it nicer:

using multiple set on the same Map can be replaced by one merge:

return game.merge({
    'isGameOver': true,
    'tiles': revealedTiles
});

Also, updating an individual tile can be done nicer:

revealedTiles = game.get('tiles').map(
    tile => tile.update('isRevealed', (v) => tile.get('isMine') || v))

so you end up with:

function revealAll(game){
    let revealedTiles

    revealedTiles = game.get('tiles').map(
        tile => tile.update('isRevealed', (v) => tile.get('isMine') || v))

    return game.merge({
        'isGameOver': true,
        'tiles': revealedTiles
    });
}

OR, you can do it like this:

const revealAll = (game) =>
    game
    .set('isGameOver', true)
    .update('tiles', (tiles) => tiles.map(
        tile => tile.update('isRevealed', (v) => tile.get('isMine') || v)))

Upvotes: 1

Related Questions