Search code examples
javascriptarraysgroup-bylodashreduce

Collapse an array of objects on a property value in javascript


How do I aggregate an array of objects on an object property in order to turn:

[ { key: 'black', value: [ '2', '3', '9' ] },
  { key: 'black', value: [ '1' ] },
  { key: 'gold', value: [ '2', '3' ] },
  { key: 'gold', value: [ '1' ] },
  { key: 'red', value: [ '9' ] },
  { key: 'white', value: [ '2', '3' ] },
  { key: 'white', value: [ '1' ] } ]

...into:

[ { key: 'black', value: [ '1', '2', '3', '9' ] },
  { key: 'gold', value: [ '1', '2', '3' ] },
  { key: 'red', value: [ '9' ] },
  { key: 'white', value: [ '1', '2', '3' ] } ]

...with javascript?

I feel like there should be a fairly straightforward way to do this with lodash or Array.reduce, but I cant for the life of me work out how to do it.


Solution

  • const dedupe = a => [...new Set(a)].sort()
    
    const arr = [
      { key: 'black', value: ['2', '3', '9', '1'] },
      { key: 'black', value: ['1'] },
      { key: 'gold', value: ['2', '3'] },
      { key: 'gold', value: ['1'] },
      { key: 'red', value: ['9'] },
      { key: 'white', value: ['3', '2'] },
      { key: 'white', value: ['1'] }
    ]
    
    dedupe(arr.map(item => item.key)) // get unique, sorted array of 'key's
      .map(key =>
        arr
          .filter(item => item.key == key) // process each key in turn
          .reduce((a, c) => ({
            key,
            value: dedupe([...c.value, ...a.value]) // aggregate and sort the 'value's for each 'key'
          }))
      )