0

I am trying to convert a nested for-loop with .map() to make it more functional. My goal to create a simple card deck. This is my working code:

 generateDeck() {
    const card = (suit, value) => {
      return suit + value;
    }
    const suits = ["S", "H", "D", "C"];
    const values = ["7", "8", "9", "10", "J", "D", "K", "A"];

    for (let s = 0; s < suits.length; s++) {
      for (let v = 0; v < values.length; v++) {
        this.deck.push(card(suits[s], values[v]))
      }
    }
  }

This is how far I came:

 generateDeck() {
    const card = (suit, value) => {
      return suit + value;
    }
    const suits = ["S", "H", "D", "C"];
    const values = ["7", "8", "9", "10", "J", "D", "K", "A"];

    let deckNew = suits.map(s => {
      values.forEach(v => { return s + v });
    });

  }

I can't get it to work. How would you avoid the nested for-loop in a clean more functional way?

Thanks!

1
  • 2
    Returning things in a for-each does nothing, you'll probably want both to be a map Commented Nov 18, 2020 at 18:55

5 Answers 5

3

You cannot do this with map, because you have to combine two arrays. But you shouldn't use flatmap either, because there is no dependency of values. What you need is an applicative functor:

const arrAp = tf => xs =>
  arrFold(acc => f =>
    arrAppend(acc)
      (arrMap(x => f(x)) (xs)))
        ([])
          (tf);

const arrMap = f => xs =>
  xs.map((x, i) => f(x, i));

const liftA2 = ({map, ap}) => f => tx => ty =>
  ap(map(f) (tx)) (ty);

const arrLiftA2 = liftA2({map: arrMap, ap: arrAp});

const arrFold = f => init => xs => {
  let acc = init;
  
  for (let i = 0; i < xs.length; i++)
    acc = f(acc) (xs[i], i);

  return acc;
};

const arrAppend = xs => ys =>
  xs.concat(ys);

// library code ^^^

const values = ["7", "8", "9", "10", "J", "D", "K", "A"];

const suits = ["S", "H", "D", "C"];

const createDeck = values => suits => [values, suits];

console.log(
  arrLiftA2(createDeck) (values) (suits));

Sign up to request clarification or add additional context in comments.

Comments

1

You are right that you can use map to make it more "functional". Map returns a new array while forEach only loops through the items. You can also omit the curly brackets to directly return a value.

 generateDeck() {
    const card = (suit, value) => suit + value;
    const suits = ["S", "H", "D", "C"];
    const values = ["7", "8", "9", "10", "J", "D", "K", "A"];

    let deckNew = suits.map(s => values.map(v => s + v));
    return deckNew;
 }

Comments

1

You can nest maps, that will result in a 2-dimensional array. Then using flatMap you can convert it to a simple array.

flatMap flattens multidimensional arrays. e.g:

[[1,2], [3,4]].flatMap(x => x)

will give [ 1, 2, 3, 4 ]

So someting like this (I didn't test it)

generateDeck() {
  const card = (suit, value) => {
    return suit + value;
  }
  const suits = ["S", "H", "D", "C"];
  const values = ["7", "8", "9", "10", "J", "D", "K", "A"];
  return suits.flatMap(
    suit => values.map(value => card(suit, value))
  );
}

Comments

1

I think you should use reduce

const card = (suit) => (value) => suit + value;

const suits = ['S', 'H', 'D', 'C'];
const values = ['7', '8', '9', '10', 'J', 'D', 'K', 'A'];

const deck = suits.reduce((prev, suit) => {
    const suitedCard = card(suit);
    return [...prev, ...values.map(suitedCard)];
}, []);

console.log(deck);

Comments

0

So, from a functional programming perspective, a good approach in my opinion is to be as declarative as possible. You don't really need to hadncraft this algorithm since outputting "a new list out of the two supplied by creating each possible pair from the lists" is a very common operation, for which a lot of implementations already exist.

const suits = ["♠︎", "♣︎", "♥︎", "♦︎"];
const values = ["7", "8", "9", "10", "J", "D", "K", "A"];
    
const createDeck = R.pipe(
  R.xprod,
  R.map(R.join('')),
);

console.log(
  createDeck(suits, values),
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.min.js" integrity="sha512-rZHvUXcc1zWKsxm7rJ8lVQuIr1oOmm7cShlvpV0gWf0RvbcJN6x96al/Rp2L2BI4a4ZkT2/YfVe/8YvB2UHzQw==" crossorigin="anonymous"></script>

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.