Search code examples
javascriptarraysforeachpoker

Nested forEach loops unexpected behavior


In my poker app I have an array of hands, each hand being array of randomly selected card objects with value and suit:

[ [ { value: 5, suit: 's' },
    { value: 4, suit: 's' },
    { value: 6, suit: 'c' },
    { value: 11, suit: 'd' },
    { value: 12, suit: 'c' } ],
  [ { value: 9, suit: 'd' },
    { value: 12, suit: 'h' },
    { value: 8, suit: 'c' },
    { value: 12, suit: 's' },
    { value: 2, suit: 's' } ],
  [ { value: 4, suit: 'h' },
    { value: 6, suit: 's' },
    { value: 10, suit: 'c' },
    { value: 3, suit: 'd' },
    { value: 7, suit: 'd' } ] ]

To prepare the hands for evaluation I want to return an array of hand objects, each with an array of values and suits. So the output would be:

[ 
  { 
    values: [5, 4, 6, 11, 12],
    suits: ['s', 's', 'c', 'd', 'c'] 
  },      
  { 
    values: [9, 12, 8, 12, 2], 
    suits: ['d', 'h', 'c', 's', 's'] 
  },
  { 
    values: [4, 6, 10, 3, 7],
    suits: ['h', 's', 'c', 'd', 'd'] 
  } 
]

I'm trying to use nested forEach loops to achieve this, like so:

let temp = []
hands.forEach((el) => {
  temp = el
  el = {}
  el.values = []
  el.suits = []
  temp.forEach((obj) => {
    el.values.push(obj.value)
    el.suits.push(obj.suit)
    console.log(el) //expected output
  })
  console.log(el) //expected output
})
console.log(hands) //same as original

However as the comments outline, it behaves as expected until the loop has finished, where hands has not changed at all. What am I missing here?


Solution

  • forEach does not change the array on which it was called, it simply iterates the array calling a function for each element. If you want to build a new array inside that function, just do so

    var newArray = [];
    hands.forEach((el) => {
       var newValue = // do something here
       newArray.push(newValue);
    });
    

    I think what you're trying to do is this:

    var hands = [ [ { value: 5, suit: 's' },
        { value: 4, suit: 's' },
        { value: 6, suit: 'c' },
        { value: 11, suit: 'd' },
        { value: 12, suit: 'c' } ],
      [ { value: 9, suit: 'd' },
        { value: 12, suit: 'h' },
        { value: 8, suit: 'c' },
        { value: 12, suit: 's' },
        { value: 2, suit: 's' } ],
      [ { value: 4, suit: 'h' },
        { value: 6, suit: 's' },
        { value: 10, suit: 'c' },
        { value: 3, suit: 'd' },
        { value: 7, suit: 'd' } ] ];
    
    let newValues = []
    hands.forEach((el) => {
      var temp = {values:[], suits:[]};
      el.forEach((obj) => {
        temp.values.push(obj.value)
        temp.suits.push(obj.suit)
        
      })
      newValues.push(temp);
    })
    console.log(newValues) // NOT same as original

    There is many ways to achieve this same thing, the best I could come up with is below - and avoids the double hand.map seen in other answers (which is pretty inefficient).

    var hands = [ [ { value: 5, suit: 's' },
        { value: 4, suit: 's' },
        { value: 6, suit: 'c' },
        { value: 11, suit: 'd' },
        { value: 12, suit: 'c' } ],
      [ { value: 9, suit: 'd' },
        { value: 12, suit: 'h' },
        { value: 8, suit: 'c' },
        { value: 12, suit: 's' },
        { value: 2, suit: 's' } ],
      [ { value: 4, suit: 'h' },
        { value: 6, suit: 's' },
        { value: 10, suit: 'c' },
        { value: 3, suit: 'd' },
        { value: 7, suit: 'd' } ] ];
    
    let newValues = hands.map(h => {
          return h.reduce( (p,c) => {
                p.values.push(c.value);
                p.suits.push(c.suit);
                return p;
            },{values:[], suits:[]});
      });
    
    console.log(newValues) // NOT same as original