Search code examples
javascriptecmascript-6reduceecmascript-5

Reduce array of objects to unique keys and add array of values common to that key


I would like to group the values of this array by its car key and then push the values common to that car into a values array.

I managed to do it with this but was wondering if there was an simpler way to do it with reduce.

const arr = [{
  car: 'audi',
  value: 'black'
}, {
  car: 'audi',
  value: 'expensive'
}, {
  car: 'fiat',
  value: 'red'
}, {
  car: 'fiat',
  value: 'cheap'
}]

// Simple array with unique car
const cars = Array.from(new Set(arr.map(({ car }) => car)))
// Array of objects with unique `car` and an empty `values` array for each
const result = cars.map((car) => ({ car, values: [] }))

// Push to values array the `value` for each car
arr.map((obj) => {
  result.map((res) => {
    if (obj.car === res.car) {
      res.values.push(obj.value)
    }
  })
})

console.log(result)
/*
[{
  car: 'audi',
  values: ['black', 'expensive']
}, {
  car: 'fiat',
  values: ['red', 'cheap']
}]
*/

Solution

  • Make an object indexed by the car name, then iterate over original array, pushing the value to the array on the object:

    const arr = [{
      car: 'audi',
      value: 'black'
    }, {
      car: 'audi',
      value: 'expensive'
    }, {
      car: 'fiat',
      value: 'red'
    }, {
      car: 'fiat',
      value: 'cheap'
    }];
    
    const carsByName = {};
    for (const { car, value } of arr) {
      if (!carsByName[car]) carsByName[car] = { car, value: [] };
      carsByName[car].value.push(value);
    }
    console.log(Object.values(carsByName));

    While this could be done with reduce, it's arguably not very semantically appropriate when the accumulator never changes (and is a bit noisy, syntactically):

    const arr = [{
      car: 'audi',
      value: 'black'
    }, {
      car: 'audi',
      value: 'expensive'
    }, {
      car: 'fiat',
      value: 'red'
    }, {
      car: 'fiat',
      value: 'cheap'
    }];
    
    const carsByName = arr.reduce((a, { car, value }) => {
      if (!a[car]) a[car] = { car, value: [] };
      a[car].value.push(value);
      return a;
    }, {});
    console.log(Object.values(carsByName));