Array.reduce on a multidimensional array to array of objects

17,354

Solution 1

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 ^_^

Solution 2

There you go - a solution using nested Array.prototype.reduce functions:

var array=[[{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'}]];

var result = array.reduce(function(p, c) {
      p.push(c.reduce(function(a, b) {
        a.values.push(b.value);
        a.suits.push(b.suit);
        return a;
      }, {values: [],suits: []}));
    return p;
  },[]);

console.log(result);
.as-console-wrapper {top: 0;max-height: 100%!important;}
Share:
17,354

Related videos on Youtube

Jackson Lenhart
Author by

Jackson Lenhart

Musician interested in coding.

Updated on June 26, 2022

Comments

  • Jackson Lenhart
    Jackson Lenhart almost 2 years

    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
    
    • charlietfl
      charlietfl over 7 years
      "I want to use reduce" ... so where is your code attempt to do so? This isn't a code writing service. This site works by you posting your code that isn't working as expected and people help you fix that
    • Jackson Lenhart
      Jackson Lenhart over 7 years
      I made code attempts, I didn't think they would be helpful to post because it seemed any valid answer would be a complete rewrite of my implementation. Fair criticism though, I'll keep it in mind.
    • Barmar
      Barmar over 7 years
      Why does this have to be done using reduce? It seems like it would be simpler using forEach.
    • Barmar
      Barmar over 7 years
      el is a variable local to the function, it gets reset to {} each time through the forEach loop. Your code never modifies hands.
  • Barmar
    Barmar over 7 years
    He wants arrays in his result, not concatenated strings.
  • Nishanth Matha
    Nishanth Matha over 7 years
    @Barmar thanks for the comment...edited the post to reflect the changes
  • Barmar
    Barmar over 7 years
    That's a silly fix. Why make a string and then split up into an array when you can just create an array in the first place?
  • Nishanth Matha
    Nishanth Matha over 7 years
    @Barmar that's a fair comment. See the edit 2 and just bring on what you got :D ...