Home > Back-end >  Merging objects based on their values
Merging objects based on their values

Time:03-22

I have an array of objects and I’m trying to combine them together into a single object, based on their values.

For example, if I have this array:

const arr = [
    {mo: true, tu: true, we: {...another object}, th: true, fr: true, sa: {...aDifferentObject}, su: true},
    {mo: {...aThirdObject}, tu: true, we: true, th: true, fr: true, sa: true, su: true}

I would then want to return:

console.dir(combineObjects(arr))
// {mo: {...aThirdObject}, tu: true, we: {...another object}, th: true, fr: true, sa: {...aDifferentObject}, su: true},

With that, I also need to decide on what to do when multiples keys of the same name have objects in them.

I've tried various methods like arr.reduce() and Object.keys(arr[0]).forEach() but reduce() returns an array and Object.keys().forEach() gets really messy really quickly.

Is there an easier way to do this? I feel like I must be overthinking it.

[EDIT: The actual input and expected output were requested in the comments]

// Input
[{fr:{ 
    beginWork: ['08', '00'],
    end: ['23', '59'],
    endWork: ['16', '15'],
    start: ['00', '00'],
    },
    mo: true,
    sa: true,
    su: {
        beginWork: ['08', '00']
        end: ['23', '59']
        endWork: ['16', '15']
        start: ['00', '00']
    },
    th: true,
    tu: {
        beginWork: ['08', '00']
        end: ['23', '59']
        endWork: ['16', '15']
        start: ['00', '00']
    },
    we: true,
},
{
    fr: {
        end: ['13', '00']
        start: ['00', '00']
    },
    mo: {
        end: ['13', '00']
        start: ['00', '00']
    },
    sa: {
        end: ['13', '00']
        start: ['00', '00']
    },
    su: {
        end: ['13', '00']
        start: ['00', '00']
    },
    th: {
        end: ['13', '00']
        start: ['00', '00']
    },
    tu: {
        end: ['13', '00']
        start: ['00', '00']
    },
    we: true
}]
// Expected output
{
    fr:{ 
        beginWork: ['08', '00'],
        end: ['23', '59'],
        endWork: ['16', '15'],
        start: ['00', '00'],
    },
    mo: {
        end: ['13', '00']
        start: ['00', '00']
    },
    sa: {
        end: ['13', '00']
        start: ['00', '00']
    },
    su: {
        beginWork: ['08', '00']
        end: ['23', '59']
        endWork: ['16', '15']
        start: ['00', '00']
    },
    th: {
        end: ['13', '00']
        start: ['00', '00']
    },
    tu: {
        beginWork: ['08', '00']
        end: ['23', '59']
        endWork: ['16', '15']
        start: ['00', '00']
    },
    we: true,
},

Please and thank you in advance :)

I hope you all have a great day!

CodePudding user response:

You can loop through reduce and add conditions on the match. You can checkout the below example. Here you can write custom logic for shouldMap based on ur need

const combine = (...args) => {
  let shouldMap = () => true;
  if (typeof args[args.length - 1] === "function") {
    shouldMap = args.pop();
  }
  return args.reduce((acc, item) => {
    Object.entries(item).forEach(([key, value]) => {
      if (shouldMap(key, acc, value)) acc[key] = value;
    });
    return acc;
  }, {});
};


const aDifferentObject = { 1: 1 };
const aThirdObject = { 2: 2 };
const arr = [
  {
    mo: true,
    tu: true,
    we: {},
    th: true,
    fr: true,
    sa: { ...aDifferentObject },
    su: true,
  },
  {
    mo: { ...aThirdObject },
    tu: true,
    we: true,
    th: true,
    fr: true,
    sa: true,
    su: true,
  },
];


console.log(
  combine(
    ...arr,
    (key, acc, value) =>
      !(typeof value !== "object" && typeof acc[key] === "object")
  )
);

A simplified version of combine:

const combine = (...args) => {
  let shouldMap = () => true;
  if (typeof args[args.length - 1] === "function") {
    shouldMap = args.pop();
  }
  let result = {};
  for (let item of args) {
    for (let key in item) {
      if (shouldMap(key, result, item[key])) result[key] = item[key];
    }
  }
  return result;
};

CodePudding user response:

Try this approach,

const arr = [
    {mo: true, tu: true, we: { test : 1}, th: true, fr: true, sa: { test: 2 }, su: true},
    {mo: {test : 3}, tu: true, we: true, th: true, fr: true, sa: true, su: true}
    ];
let result = arr[0];
arr.forEach(element => {
    Object.keys(result).forEach(key => {
        if(typeof(element[key]) != 'boolean'){
            result[key] = element[key];
        }
    });
});   
console.log(result);

CodePudding user response:

I would suggest using Array.reduce() to combine your array elements into one object.

As we enumerate each element in the array, we'll replace any property of the result if it is not an object.

const input = [{fr:{ beginWork: ['08', '00'], end: ['23', '59'], endWork: ['16', '15'], start: ['00', '00'], }, mo: true, sa: true, su: { beginWork: ['08', '00'], end: ['23', '59'], endWork: ['16', '15'], start: ['00', '00'] }, th: true, tu: { beginWork: ['08', '00'], end: ['23', '59'], endWork: ['16', '15'], start: ['00', '00'] }, we: true, }, { fr: { end: ['13', '00'], start: ['00', '00'] }, mo: { end: ['13', '00'], start: ['00', '00'] }, sa: { end: ['13', '00'], start: ['00', '00'] }, su: { end: ['13', '00'], start: ['00', '00'] }, th: { end: ['13', '00'], start: ['00', '00'] }, tu: { end: ['13', '00'], start: ['00', '00'] }, we: true }]

function isObject(input) { 
    return input && (typeof(input) === 'object');
}

const result = input.reduce((acc, el) => { 
   return Object.keys(el).reduce((acc, key) => {
       // Don't replace if it's an object...
       return isObject(acc[key]) ? acc: { ...acc, [key]: el[key] };
   }, acc)
}, {});

console.log('Result:', result)
.as-console-wrapper { max-height: 100% !important; }

  • Related