2
var dictA = { male: 10, female: 20, unassigned: 30 };
var dictB = { male: 11, female: 21, unassigned: 31 };
var dictC = { male: 12, female: 22, unassigned: 32 };

Is there an easier way than multiple loops to produce a result like follows:

{
    male: [10, 11, 12],
    female: [20, 21, 22],
    unassigned: [30, 31, 32]
}

I am not sure if 'combine' is the right word here.

Steven Floyd
  • 353
  • 1
  • 3
  • 11
  • FYI, in JS we usually call these Objects, not dictionaries or lists :) – JBis Dec 04 '19 at 03:12
  • See also: https://stackoverflow.com/questions/59122448/create-an-object-based-on-the-keys-in-array-of-objects/59123350 – JLRishe Dec 04 '19 at 04:26
  • Does this answer your question? [How to group an array of objects by key](https://stackoverflow.com/questions/40774697/how-to-group-an-array-of-objects-by-key) – alexyorke Dec 04 '19 at 04:38

5 Answers5

6

This still requires a couple loops, but perhaps a combination of reduce and for...in will get this done eloquently:

var dictA = { male: 10, female: 20, unassigned: 30 };
var dictB = { male: 11, female: 21, unassigned: 31 };
var dictC = { male: 12, female: 22, unassigned: 32 };

const res = [dictA, dictB, dictC].reduce((acc, el) => {
  for (let key in el) {
    acc[key] = [...acc[key] || [], el[key]];
  };
  return acc;
}, {})

console.log(res);
Nick
  • 16,066
  • 3
  • 16
  • 32
  • I'm happy to explain whatever is confusing to you @JBis – Nick Dec 04 '19 at 03:01
  • Well it works and it's lean. If there is a more 'readable' answer I am surely open to it - because at the end of the day, I'd rather this than multiple loops (and honestly, this is more readable). And be it that I am unfamiliar with some of what is going on - I am sure I can find out with a little more digging. That was a fast response, thanks @Nick. – Steven Floyd Dec 04 '19 at 03:02
  • @StevenFloyd no problem! This is technically two loops (`reduce` is a loop and the `for...in` is a loop). That being said, a lot of the other solutions have three loops, which definitely seems excessive – Nick Dec 04 '19 at 03:21
0

This works assuming dictA has all of the necessary properties. It's not as functional like @Nicks.

const dictA = { male: 10, female: 20, unassigned: 30 };
const dictB = { male: 11, female: 21, unassigned: 31 };
const dictC = { male: 12, female: 22, unassigned: 32 };
    
const obj = {};

Object.keys(dictA).forEach(key => {
   obj[key] = [dictA,dictB,dictC].map(dict => dict[key]);
});

console.log(obj);
JBis
  • 827
  • 1
  • 10
  • 26
0

const addToBucket = (bucket, [k, v], prev = bucket[k] || []) => ({
  ...bucket, [k]: [...prev, v]
})  

const bucket = list => list.flatMap(Object.entries).reduce(
  (bucket, entry) => addToBucket(bucket, entry), {}
)

// ---------------------------------------------------------- //
console.log(bucket([
  { male: 10, female: 20, unassigned: 30 },
  { male: 11, female: 21, unassigned: 31 },
  { male: 12, female: 22, unassigned: 32 },
]))
kornieff
  • 2,389
  • 19
  • 29
0
var dictA = { male: 10, female: 20, unassigned: 30 };
var dictB = { male: 11, female: 21, unassigned: 31 };
var dictC = { male: 12, female: 22, unassigned: 32 };

var input = [dictA, dictB, dictC];
var output = [];
input.forEach(function(item) {
  var existing = output.filter(function(v, i) {
    return v.name == item.name;
  });
  if (existing.length) {
    var existingIndex = output.indexOf(existing[0]);
    // output[existingIndex].value = output[existingIndex].value.concat(item.value);
  } else {
    if (typeof item.value == 'string')
      item.value = [item.value];
    output.push(item);
  }
});

alert(output[0].male);
KpStar
  • 122
  • 7
0

You could do it with .reduce() and .forEach() too

let result = [dictA, dictB, dictC].reduce((rv, dict) => (
    Object.keys(dict).forEach(key => (
        rv[key] != null ? rv[key].push(dict[key]) : rv[key] = [dict[key]]
    )),
    rv
), {});
Pointy
  • 405,095
  • 59
  • 585
  • 614