javascriptarraysfunctional-programmingpoker

Array.reduce on a multidimensional array to array of objects


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 use Array.reduce to return an array of hand objects. 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 tried implementing this with nested forEach's, but its failing and I don't know why. I have two console.log's within which output as expected, but in the end hands is identical to the input.

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

Solution

  • You have to be thinking about the shape of your input data (DATA) and output (DATA')

    enter image description here

    Note 1:1 relationship between HAND and HAND' meaning we will use Array.prototype.map for one transformation. On the other hand, CARD has a N:1 relationship with HAND' meaing we will use Array.prototype.reduce for that transformation

    enter image description here

    So keep in mind while we're working, we will be doing a map and a reduce

    const data = 
      [ [ { 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 output = 
      data.map(cards =>
        cards.reduce(({values, suits}, {value, suit}) => ({
          values: [...values, value],
          suits: [...suits, suit]
        }), {values: [], suits: []}))
    
    console.log(output)

    Now of course that looks a little dense so it would be nice if we could dial down the complexity a bit. By making some curried adapters for map and reduce we can express a function that performs your transformation quite nicely

    const data = 
      [ [ { 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' } ] ]
    
    
    const map = f => xs => xs.map(f)
    
    const reduce = f => y => xs => xs.reduce(f, y)
    
    const handAppendCard = ({values, suits}, {value, suit}) => ({
      values: [...values, value],
      suits: [...suits, suit]
    })
    
    const makeHands =
      map (reduce (handAppendCard) ({values:[], suits:[]}))
    
    let output = makeHands (data)
    
    console.log(output)

    That's just one way to approach the problem. I hope you were able to learn something from it ^_^