2

I am looking for a simple way to do the following. I have tried to do this with lodash.reduce and it is clunky, is there an easier way.

From:

[{a: 'meow'}, {a: 'woof'}]

To:

{a: ['meow', 'woof']}
ThomasReggi
  • 55,053
  • 85
  • 237
  • 424

5 Answers5

2

You can do that with pure JS, no need of loadash.

Call the reduce method of arrays on your input array, and reduce the array to an object, looping over the keys of your inner objs:

const input = [{a: 'meow'}, {a: 'woof'}, {b: 'hi'}, {a: 'dog', c: 'bye'}, {}];

console.log(input.reduce((acc, val) => {
  Object.keys(val).forEach(key => {
    if(!acc[key]) {
      acc[key] = [];
    }
    acc[key].push(val[key]);
  });
  return acc;
}, {}));
Shidersz
  • 16,846
  • 2
  • 23
  • 48
quirimmo
  • 9,800
  • 3
  • 30
  • 45
  • 1
    I have added more objects to the input set, just to show this works over a generalized set. +1. You can rollback if you dislike this change. I apologize in that case – Shidersz Jan 03 '19 at 03:45
2

You can use lodash#assignWith to assign all properties their respective values into one object, together with a customizer function to determine how you want to structure the object.

const result = _.assignWith({}, ...data, (v = [], s) => v.concat(s));

Note: To make sure that we don't mutate any of the objects in the data array, I passed an empty object as the first parameter to act as the destination object.

const data = [
  { a: 'meow' },
  { a: 'woof', k: 'hey' },
  { k: 'yo', d: 'hehe' },
  { d: 'wazup', q: 'ohoho' }
];

const result = _.assignWith({}, ...data, (v = [], s) => v.concat(s));

console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
ryeballar
  • 29,658
  • 10
  • 65
  • 74
0

I had some issues with typescript and lodash.reduce, this worked.

export function getFuncsObject(funcs): Record<Funcs, Case[]> {
  let f = { ...funcs };
  f = lodash.mapValues(f, () => []);
  return f;
}

export function mockMerge(funcs, mocks: Record<Funcs, Case | null>[]): Record<Funcs, Case[]> {
  const f = getFuncsObject(funcs);
  lodash.each(mocks, (v, k) => {
    f[k].push(v);
  });
  return f;
}
ThomasReggi
  • 55,053
  • 85
  • 237
  • 424
0

One option would be to use two reductions as follows:

const input = [{
  a: 'meow'
}, {
  a: 'woof'
}, {
  b: 'moo'
}];

const result = input
.reduce((itemResult, item) => Object.keys(item)
.reduce((keyResult, key) => ({
  ...keyResult,
  [key]: (keyResult[key] || []).concat(item[key])
}), itemResult), {});

console.log(result)

Not sure if this is clunky compared to your current solution, but it's fairly concise and does not require an external library.

Dacre Denny
  • 29,664
  • 5
  • 45
  • 65
0

Without using any external libraries or reduce.

const input = [ {a: 'meow'}, {a: 'woof'}, {b: 'hi'}, {a: 'dog', c: 'bye'}, {} ];
let output = {};

input.forEach((inputObj) => {
  for(let key in inputObj){
    if(!output[ key ]){
      output[ key ] = [];
    }
    output[ key ].push(inputObj[key])
  }
});


console.log(output);
uniquerockrz
  • 231
  • 3
  • 9