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:
- if
typefield is missing, we would need to add it and make itundefined - if
amountfield is missing, we need to add it and make it0
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()]);