WongSifu
WongSifu

Reputation: 547

Reduce Single Array of Objects using two properties of an Object

I have a single array of objects and I would like to reduce it down to another array of objects based on two keys value pairs.

const original =
    [
        {
            key1: 'a',
            key2: 'AA',
            value: 1
        },
        {
            key1: 'a',
            key2: 'AA',
            anotherValue: 2
        },
        {
            key1: 'b',
            key2: 'BB',
            value: 1
        },
        {
            key1: 'a',
            key2: 'AA',
            yetAnother: 3
        },
        {
            key1: 'b',
            key2: 'BB',
            anotherValue: 4
        },
        {
            key1: 'c',
            key2: 'CC',
            value: 1
        }
    ];

Should be transformed into:

const result =
    [
        {
            key1: 'a',
            key2: 'AA',
            value: 1,
            anotherValue: 2,
            yetAnother: 3
        },
        {
            key1: 'b',
            key2: 'BB',
            value: 1,
            anotherValue: 4
        },
        {
            key1: 'c',
            key2: 'CC',
            value: 1
        },
    ];

Tired using map and reduce and even lodash. However, all my attempts were futile.

Upvotes: 0

Views: 987

Answers (2)

Phil
Phil

Reputation: 164736

Using Array.prototype.reduce(), build up a Map of entries, keyed by the combination of any key* properties while merging in the other properties.

Then you can convert the Map values into a new array

const original = [{"key1":"a","key2":"AA","value":1},{"key1":"a","key2":"AA","anotherValue":2},{"key1":"b","key2":"BB","value":1},{"key1":"a","key2":"AA","yetAnother":3},{"key1":"b","key2":"BB","anotherValue":4},{"key1":"c","key2":"CC","value":1}]

const t1 = performance.now()

const result = [...original.reduce((map, obj) => {
  // create a key from all the "key*" properties
  const key = Object.keys(obj)
    .filter(key => key.startsWith("key"))
    .sort()
    .map(key => obj[key]).join(":") // looks like "a:AA", "b:BB", etc
    
  // merge with previously found matches
  const entry = {
    ...(map.get(key) ?? {}),
    ...obj
  }
  
  // collect the merged object
  return map.set(key, entry)
}, new Map()).values()]

const t2 = performance.now()
console.info(result, `in ${t2 - t1}ms`)
.as-console-wrapper { max-height: 100% !important; }

Upvotes: 0

Derek Wang
Derek Wang

Reputation: 10193

Using Array.reduce, you can group the objects by key1 and key2 value pairs. And the variable groupBy object values contain the result you want and you can generate the values only using Object.values(groupBy).

const original = [
  { key1: 'a', key2: 'AA', value: 1 },
  { key1: 'a', key2: 'AA', anotherValue: 2 },
  { key1: 'b', key2: 'BB', value: 1 },
  { key1: 'a', key2: 'AA', yetAnother: 3 },
  { key1: 'b', key2: 'BB', anotherValue: 4 },
  { key1: 'c', key2: 'CC', value: 1 }
];

const groupBy = original.reduce((acc, cur) => {
  const { key1, key2, ...rest } = cur;
  const key = key1 + "_" + key2;
  acc[key] ? acc[key] = { ...acc[key], ...rest } : acc[key] = cur;
  return acc;
}, {});

const result = Object.values(groupBy);
console.log(result);

Upvotes: 2

Related Questions