Home > Mobile >  How to elegantly merge multiple objects with overlapping keys?
How to elegantly merge multiple objects with overlapping keys?

Time:11-26

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 },
};

CodePudding user response:

  • 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);

CodePudding user response:

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)

CodePudding user response:

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; }

CodePudding user response:

const merged_tests = {}

for (const i of Array(52).keys()) {
  merged_tests[`week${i}`] = {
    ...ab_tests[`week${i}`],
    ...cd_tests[`week${i}`],
    ...xy_tests[`week${i}`]
  }
}

Is this what you need or do you need something even more elegant?

CodePudding user response:

flatMap(Object.entries) flattens things out and makes it a little more readable in my opinion.

function merge(tests) {
  const entries = tests.flatMap(Object.entries);
  return entries.reduce((merged, [week, values]) => {
    Object.assign(merged[week] ??= {}, values);
    return merged;
  }, {});
}

console.log(merge([ab_tests, cd_tests, xy_tests]));
  • Related