Gokkul
Gokkul

Reputation: 84

Combine two objects in an array into a single object if the object properties are the same and convert the unique properties into an array

Input -

[
  {color:'red', shape:'circle', size:'medium'},
  {color:'red', shape:'circle', size:'small'}
]

Output -

[
  {color:'red', shape:'circle', size:['medium','small']}
]

How can this be achieved in Javascript?

Upvotes: 1

Views: 152

Answers (3)

Akrion
Akrion

Reputation: 18525

Here is a simple groupBy function which accepts an array of objects and array of props to group on:

let data = [
  {color:'red', shape:'circle', size:'medium'},
  {color:'red', shape:'circle', size:'small'}
]

let groupBy = (arr, props) => Object.values(arr.reduce((r,c) => {
  let key = props.reduce((a,k) => `${a}${c[k]}`, '')
  let otherKeys = Object.keys(c).filter(k => !props.includes(k))
  r[key] = r[key] || {...c, ...otherKeys.reduce((a,k) => (a[k] = [], a),{})}
  otherKeys.forEach(k => r[key][k].push(c[k]))
  return r
}, {}))

console.log(groupBy(data, ['color','shape']))

The idea is to use Array.reduce and basically create a string key of the passed in props. For the other fields create an array and keep pushing values there on each iteration.

Upvotes: 1

adiga
adiga

Reputation: 35253

If you just want to group based on color and shape, you could use reduce. Create an accumulator object with each unique combination of those 2 properties separated by a | as key. And the object you want in the output as their value. Then use Object.values() to get those objects as an array.

const input = [
  {color:'red', shape:'circle', size :'medium'},
  {color:'red', shape:'circle', size:'small'},
  {color:'blue', shape:'square', size:'small'}
];

const merged = input.reduce((acc, { color, shape, size }) => {
  const key = color + "|" + shape;
  acc[key] = acc[key] || { color, shape, size: [] };
  acc[key].size.push(size);
  return acc
}, {})

console.log(Object.values(merged))

This is what the merged/accumulator looks like:

{
  "red|circle": {
    "color": "red",
    "shape": "circle",
    "size": [
      "medium",
      "small"
    ]
  },
  "blue|square": {
    "color": "blue",
    "shape": "square",
    "size": [
      "small"
    ]
  }
}

You can make it dynamic by creating an array of keys you'd want to group by:

const input = [
      { color: 'red', shape: 'circle', size: 'medium' },
      { color: 'red', shape: 'circle', size: 'small' },
      { color: 'blue', shape: 'square', size: 'small' }
   ];
const groupKeys = ['color', 'shape'];

const merged = input.reduce((acc, o) => {
  const key = groupKeys.map(k => o[k]).join("|");
  
  if (!acc[key]) {
    acc[key] = groupKeys.reduce((r, k) => ({ ...r, [k]: o[k] }), {});
    acc[key].size = []
  }
  
  acc[key].size.push(o.size)
  return acc
}, {})

console.log(Object.values(merged))

Upvotes: 1

Maheer Ali
Maheer Ali

Reputation: 36594

You can create a general function which takes an array of objects and array of keys to match as its parameters.

You can do that in following steps:

  • First use reduce() on the array of objects and set accumulator to empty array []
  • Get the other props(unique props) by using filter() on Object.keys()
  • Then in each iteration find the element of the accumulator array whose all the given props matches with the current object.
  • If the element is found then use forEach() other keys and push the values to to corresponding array.
  • If element is not found then set each key to an empty array.
  • At last return the result of reduce()

const arr = [
  {color:'red', shape:'circle', size:'medium'},
  {color:'red', shape:'circle', size:'small'}
]

function groupByProps(arr,props){
  const res = arr.reduce((ac,a) => {
    let ind = ac.findIndex(b => props.every(k => a[k] === b[k]));
    let others = Object.keys(a).filter(x => !props.includes(x));
    if(ind === -1){
      ac.push({...a});
      others.forEach(x => ac[ac.length - 1][x] = []);
      ind = ac.length - 1
    }
    others.forEach(x => ac[ind][x].push(a[x]));
    return ac;
  },[])
  return res;
}

const res = groupByProps(arr,['color','shape'])
console.log(res)

Upvotes: 1

Related Questions