Rio0o.dev
Rio0o.dev

Reputation: 25

Combine two arrays of objects

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));

Upvotes: 0

Views: 95

Answers (4)

ArnaudV
ArnaudV

Reputation: 341

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 ) ?? {})}]
}, [])

Upvotes: 0

Ben
Ben

Reputation: 668

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));

Upvotes: 0

Nina Scholz
Nina Scholz

Reputation: 386868

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; }

Upvotes: 3

Melchia
Melchia

Reputation: 24324

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)).

Upvotes: 0

Related Questions