Skip to content
Advertisement

JavaScript: how can I merge these two arrays of incomplete objects and make an array of complete objects

I have two arrays of objects

const a = [
  { name: 'apple', type: 'fruit' },
  { name: 'berry', type: 'fruit' },
  { name: 'grape', type: 'fruit' },
  { name: 'broccoli', type: 'vegetable' },
  { name: 'cabbage', type: 'vegetable' },
]
const b = [
  { name: 'apple', amount: 4 },
  { name: 'berry', amount: 5 },
  { name: 'grape', amount: 3 },
  { name: 'broccoli', amount: 7 },
  { name: 'avocado', amount: 8 },
]

I need to write a function to output an array with objects with the same name being merged into one.

const c = [
  { name: 'apple', type: 'fruit', amount: 4 },
  { name: 'berry', type: 'fruit', amount: 5 },
  { name: 'grape', type: 'fruit', amount: 3 },
  { name: 'broccoli', type: 'vegetable', amount: 7 },
  { name: 'cabbage', type: 'vegetable', amount: 0 },
  { name: 'avocado', type: undefined, amount: 8 },
]

As you can see here, objects that share the same name are merged into one object with a few exceptions:

  1. if type field is missing, we would need to add it and make it undefined
  2. if amount field is missing, we need to add it and make it 0

Here is my attempt:

function fillMissingFields(object) {
  console.log('object', object)
  let newObject = { ...object }
  if (object.type === undefined) {
    newObject = { ...object, type: undefined }
  }
  if (object.amount === undefined) {
    newObject = { ...newObject, amount: 0 }
  }
  return newObject
}

function join(a, b) {
  const results = []
  for (const aItem of a) {
    const bItems = b.filter((item) => item.name === aItem.name)
    let newObject
    if (bItems.length) {
      for (const bItem of bItems) {
        newObject = { ...newObject, ...bItem }
      }
      newObject = fillMissingFields({ ...newObject, ...aItem })
    } else {
      newObject = fillMissingFields(aItem)
    }
    results.push(newObject)
  }
  return results
}

Besides the fact that it has a really bad time complexity O(n^2). It actually has a bug where if an object only appears in b array, that object will be omitted entirely from the new array.

Can anyone try to help me come up with a more robust and efficient algorithm to tackle this problem?

Advertisement

Answer

Make a collection whose keys are the names, whose values are the combined objects, which starts out with an undefined type and an amount of 0. Iterate through both arrays, assigning the property values as needed, then at the end, take the collection’s values:

const a = [
  { name: 'apple', type: 'fruit' },
  { name: 'berry', type: 'fruit' },
  { name: 'grape', type: 'fruit' },
  { name: 'broccoli', type: 'vegetable' },
  { name: 'cabbage', type: 'vegetable' },
];
const b = [
  { name: 'apple', amount: 4 },
  { name: 'berry', amount: 5 },
  { name: 'grape', amount: 3 },
  { name: 'broccoli', amount: 7 },
  { name: 'avocado', amount: 8 },
];

const objsByName = new Map();
const getObj = (name) => {
  if (!objsByName.has(name)) {
    objsByName.set(name, { name, type: undefined, amount: 0 });
  }
  return objsByName.get(name);
};
for (const { name, type } of a) {
  getObj(name).type = type;
}
for (const { name, amount } of b) {
  getObj(name).amount = amount;
}
console.log([...objsByName.values()]);
Advertisement