benito_h
benito_h

Reputation: 550

How to elegantly merge multiple objects with overlapping keys?

Let's consider multiple objects with overlapping keys, where each key indicates a week of the year and the values are objects of integer test results, like

const ab_tests = { week1: { a: 4, b: 6 }, week2: { a: 0, b: 9 } };
const cd_tests = { week2: { c: 2, d: 5 }, week3: { c: 6, d: 7 } };
const xy_tests = { week1: { x: 1, y: 1 }, week4: { x: 100, y: 123 } };

What is an elegant way to merge them to a single object that contains all weeks as keys and the values as merged-objects, such that:

const merged_tests = {
  week1: { a: 4, b: 6, x: 1, y: 1 },
  week2: { a: 0, b: 9, c: 2, d: 5 },
  week3: { c: 6, d: 7 },
  week4: { x: 100, y: 123 },
};

Upvotes: 3

Views: 616

Answers (4)

Trevor Dixon
Trevor Dixon

Reputation: 24342

flatMap(Object.entries) flattens things so there's only one loop to read, making it a little more readable in my opinion.

function merge(...tests) {
  return tests.flatMap(Object.entries).reduce(
      (obj, [k, v]) => Object.assign(obj, {[k]: {...obj[k], ...v}}), {});
}

console.log(merge(ab_tests, cd_tests, xy_tests));

Upvotes: 1

adiga
adiga

Reputation: 35222

You could loop through the key of each object and update an output object with the same key

const inputs = [ab_tests, cd_tests, xy_tests],
      output = { }

for (const o of inputs) {
  for (const key in o)
    Object.assign(output[key] ??= {}, o[key])
}

Here's a snippet:

const ab_tests = { week1: { a: 4, b: 6 }, week2: { a: 0, b: 9 } },
      cd_tests = { week2: { c: 2, d: 5 }, week3: { c: 6, d: 7 } },
      xy_tests = { week1: { x: 1, y: 1 }, week4: { x: 100, y: 123 } },
      inputs = [ab_tests, cd_tests, xy_tests],
      output = {}

for (const o of inputs) {
  for (const key in o)
    Object.assign(output[key] ??= {}, o[key])
}

console.log(output)

Upvotes: 2

Nina Scholz
Nina Scholz

Reputation: 386560

You could reduce the array of objects by grouping with the keys of the outer objects.

const
    merge = array => array.reduce((r, o) => Object
        .entries(o)
        .reduce((t, [k, q]) => {
            Object.assign(t[k] ??= {}, q);
            return t;
        }, r),
    {}),
    ab_tests = { week1: { a: 4, b: 6 }, week2: { a: 0, b: 9 } },
    cd_tests = { week2: { c: 2, d: 5 }, week3: { c: 6, d: 7 } },
    xy_tests = { week1: { x: 1, y: 1 }, week4: { x: 100, y: 123 } },
    result = merge([ab_tests, cd_tests, xy_tests]);

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Upvotes: 0

Majed Badawi
Majed Badawi

Reputation: 28414

  • Using Array#reduce, iterate over the objects while updating the final one (accumulator)
  • In each iteration, using Object#entries and Array#forEach, iterate over the pairs of the current object and update the final one

const ab_tests = { week1: { a: 4, b: 6 }, week2: { a: 0, b: 9 } };
const cd_tests = { week2: { c: 2, d: 5 }, week3: { c: 6, d: 7 } };
const xy_tests = { week1: { x: 1, y: 1 }, week4: { x: 100, y: 123 } };

const merged = [ab_tests, cd_tests, xy_tests].reduce((merged, current) => {
  Object.entries(current).forEach(([key, value]) => {
    merged[key] ??= {};
    merged[key] = { ...merged[key], ...value };
  });
  return merged;
}, {});

console.log(merged);

Upvotes: 4

Related Questions