I have such case: I'm pushing to object new key-value pairs, like:

let a1 = {key1: 'val1'};
let a2 = {key2: 'val2'};
const result = {...a1, ...a2};

and as a result I get a valid new object: {key1: 'val1', key2: 'val2'}.

but there is a third case:

let a1 = {key1: 'val1'};
let a2 = {key2: 'val2'};
let b1 = {key1: 'val1b'};
const result = {...a1, ...a2, ...b1};

and as a result I should get:

{key1: ['val1', 'var1b'], key2: 'val2'}`.

so -> if there is already such a key -> transform this key values to array and push them.

I tried so:

for... all of my objects to push:

const keysToPush = Object.keys(b1);
const keysInitial = Object.keys(a1); // foreach too
if (keysInitial.includes(keysToPush)) // push as array

but it looks to ugly

probably there should be more elegant solution?

4 Answers

1
Nina Scholz On

You need to look at each property and decide if to assign or convert to array and push the value.

const
    migrate = (target, source) => {
        Object.keys(source).forEach(key => {
            if ([].concat(target[key] || []).includes(source[key])) return;
            if (key in target) {
                if (!Array.isArray(target[key])) target[key] = [target[key]];
                target[key].push(source[key]);
            } else {
                target[key] = source[key];
            }
        });
        return target;
    };

var a1 = { key1: 'val1' },
    a2 = { key2: 'val2' },
    b1 = { key1: 'val1b' },
    b2 = { key1: 'val1b' },
    result = [a1, a2, b1, b2].reduce(migrate, {});

console.log(result);

0
Maheer Ali On

You can create a function to merge objects.The steps of solution are:

  • Create a function which accepts any number of objects using rest parameters
  • Create an empty object which will be result.
  • Loop thorough all the objects using forEach. Inside forEach create a another nested forEach to loop through entries of object.
  • Check if the key already exists in result obj.

    • Check if its values is an array then push new value to it.

    • If its not array just convert it to array of two values

  • If the key doesnot exist that add it normally as key of object.

function mergeObjs(...objs){
  const res = {};
  objs.forEach(x => {
    Object.entries(x).forEach(([k,v]) => {
      if(res[k]){
        if(Array.isArray(res[k])) res[k].push(v)
        else res[k] = [res[k],v]
      }
      else res[k] = v;
    })
  })
  return res;
}

let a1 = {key1: 'val1'};
let a2 = {key2: 'val2'};
let b1 = {key1: 'val1b'};

console.log(mergeObjs(a1,a2,b1))

1
Jack Bashford On

Check if the key exists. If it does, check if it's an array, and handle each case accordingly:

let a1 = {
  key1: 'val1'
};
let a2 = {
  key2: 'val2'
};
let b1 = {
  key1: 'val1b'
};

const merge = (...o) => {
  let res = o.reduce((acc, curr) => {
    Object.entries(curr).forEach(([k, v]) => {
      acc[k] = (acc[k] && Array.isArray(acc[k]) ? [...acc[k], v] : (acc[k] ? [acc[k], v] : v));
    });
    return acc;
  });
  return res;
};

let result = merge(a1, a2, b1);
console.log(result);
.as-console-wrapper {
  max-height: 100% !important;
  top: auto;
}

1
trincot On

I would suggest the use of Map (for the keys) of Set (for the values per key), as the Set will ensure no duplicate values. At the end of the process the Map can be converted to a plain object, and the Set to either a single value or an array of values:

// Sample input with some repetition:
const objects = [
    { key1: 'val1' },
    { key2: 'val2' },
    { key1: 'val1b' },
    { key2: 'val2' },
    { key1: 'val1b' }
];

const pairs = objects.flatMap(Object.entries);
const map = new Map(pairs.map(([k]) => [k, new Set]));
pairs.forEach(([k, v]) => map.get(k).add(v));
const result = Object.fromEntries(Array.from(map, ([k, set]) => 
   set.size < 2 ? [k, ...set] : [k, [...set]]
));

console.log(result);