I am trying to combine these two arrays of objects together and get the output below.
userid
property should be unique but not name
or role
Input
const first = [
{ userid: 2, name: "Velen" },
{ userid: 56, name: "Illidan" },
{ userid: 23, name: "Muradin" },
{ userid: 12, name: "Sylvanas" },
{ userid: 44, name: "Cenarius" },
{ userid: 4, name: "Gul'Dan" },
];
const second = [
{ userid: 2, role: "Mage" },
{ userid: 4, role: "Worlock" },
{ userid: 56, role: "Demon Hunter" },
{ userid: 66, role: "Druid" },
{ userid: 87, role: "Shaman" },
{ userid: 12, role: "Hunter" },
];
Output
[
{ name: 'Velen', role: 'Mage', userid: 2 },
{ name: "Gul'Dan", role: 'Worlock', userid: 4 },
{ name: 'Sylvanas', role: 'Hunter', userid: 12 },
{ name: 'Muradin', role: null, userid: 23 },
{ name: 'Cenarius', role: null, userid: 44 },
{ name: 'Illidan', role: 'Demon Hunter', userid: 56 },
{ name: null, role: 'Druid', userid: 66 },
{ name: null, role: 'Shaman', userid: 87 }
]
I tried this solution but it didn't work:
const solution = (first, second) => {
first.sort((a, b) => a.userid - b.userid);
second.sort((a, b) => a.userid - b.userid);
first.map((item, idx) => {
return {
a: (item.role = second[idx].role),
b: (item.userid = second[idx].userid),
};
});
return first;
};
console.log(solution(first, second));
CodePudding user response:
You could reduce with an object and take a default object with nullified values.
const
first = [{ userid: 2, name: "Velen" }, { userid: 56, name: "Illidan" }, { userid: 23, name: "Muradin" }, { userid: 12, name: "Sylvanas" }, { userid: 44, name: "Cenarius" }, { userid: 4, name: "Gul'Dan" }],
second = [{ userid: 2, role: "Mage" }, { userid: 4, role: "Worlock" }, { userid: 56, role: "Demon Hunter" }, { userid: 66, role: "Druid" }, { userid: 87, role: "Shaman" }, { userid: 12, role: "Hunter" }],
result = Object.values([...first, ...second].reduce((r, o) => {
Object.assign(r[o.userid] ??= { name: null, role: null }, o);
return r;
}, {}));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
CodePudding user response:
In this kind of situation it's great to use Maps/Dictionary/ (key/value) data structure in general. Where the key is the userid in this situation and the value are the attributes you want to store.
const first = [
{ userid: 2, name: "Velen" },
{ userid: 56, name: "Illidan" },
{ userid: 23, name: "Muradin" },
{ userid: 12, name: "Sylvanas" },
{ userid: 44, name: "Cenarius" },
{ userid: 4, name: "Gul'Dan" },
];
const second = [
{ userid: 2, role: "Mage" },
{ userid: 4, role: "Worlock" },
{ userid: 56, role: "Demon Hunter" },
{ userid: 66, role: "Druid" },
{ userid: 87, role: "Shaman" },
{ userid: 12, role: "Hunter" },
];
const solution = (first, second) => {
const combined = new Map();
for (const item of first) {
combined.set(item.userid, { ...item, role: null });
}
for (const item of second) {
if (combined.has(item.userid)) {
combined.get(item.userid).role = item.role;
} else {
combined.set(item.userid, { ...item, name: null });
}
}
return Array.from(combined.values()).sort((a, b) => a.userid - b.userid);
};
console.log(solution(first, second));
In the end we sort using userid like what you tried to do.
Without the sort the time complexity of the solution is O(n)
. With the sort it's O(n*log(n))
.
CodePudding user response:
This is the best I could think of using reduce, had to use two of them to achieve the wanted result. Assuming the first array always contains userid
and name
while the second array always contains userid
and role
.
const first = [
{ userid: 2, name: "Velen" },
{ userid: 56, name: "Illidan" },
{ userid: 23, name: "Muradin" },
{ userid: 12, name: "Sylvanas" },
{ userid: 44, name: "Cenarius" },
{ userid: 4, name: "Gul'Dan" },
];
const second = [
{ userid: 2, role: "Mage" },
{ userid: 4, role: "Worlock" },
{ userid: 56, role: "Demon Hunter" },
{ userid: 66, role: "Druid" },
{ userid: 87, role: "Shaman" },
{ userid: 12, role: "Hunter" },
];
const solution = (first, second) => {
const firstReduce = first.reduce((acc, curr) => {
const roleElem = second.find(elem => elem.userid === curr.userid);
if (roleElem) {
return [...acc, {...curr, role: roleElem.role}];
}
return [...acc, {...curr, role: null}];
}, []);
const secondReduce = second.reduce((acc, curr) => {
const elem = firstReduce.find(elem => elem.userid === curr.userid);
if (!elem) {
return [...acc, {...curr, name: null}];
}
return acc;
}, firstReduce);
return secondReduce;
}
console.log(solution(first, second));
CodePudding user response:
You could do it using a reduce
function as follow:
// adding empty model to make sure we'll get all keys
const emptyObj = {role: null, name: null}
const emptyObj = {role: null, name: null}
const userids = [...new Set([...first, ...second].map(item => item.userid))]
const output = userids.reduce((acc, cur) => {
return [...acc, {...emptyObj, ...cur, ...(second.find(({userid}) => userid === cur ) ?? {}), ...(first.find(({userid}) => userid === cur ) ?? {})}]
}, [])