marek8623
marek8623

Reputation: 97

Remove duplicates from nested array in array of objects

I have this array of objects:

const arrayOfObjects = [{
    id: 10,
    children: [1000]
  },
  {
    id: 10,
    children: [2000]
  },
  {
    id: 20,
    children: [1000]
  },
  {
    id: 20,
    children: [1000, 2000]
  },
  {
    id: 20,
    children: [2000]
  },
];

I want to remove duplicates using this code:

  const arrayHashMap = arrayOfObjects.reduce((obj, item) => {
    if (obj[item.id]) {
      // obj[item.id].children.push(...item.children);
      const temporaryArray = [...obj[item.id].children, ...item.children];
      obj[item.id].children = [...new Set(temporaryArray)];
    } else {
      obj[item.id] = {
        ...item
      };
    }
    return obj;
  }, {});
  const result = Object.values(arrayHashMap);

In this code I commented part where I push values to array. I tried to use "new Set" to remove duplicates from final array, but I am always assigning the value to "obj[item.id].children". Is this OK or is there a better way to write this?

Expected result:

[{
  id: 10,
  children: [1000, 2000]
}, {
  id: 20,
  children: [1000, 2000]
}]

Thanks

Upvotes: 0

Views: 832

Answers (7)

shabs
shabs

Reputation: 718

i dont know about "better", but perhaps terser:

const arrayOfObjects = [{
    id: 10,
    children: [1000]
  },
  {
    id: 10,
    children: [2000]
  },
  {
    id: 20,
    children: [1000]
  },
  {
    id: 20,
    children: [1000, 2000]
  },
  {
    id: 20,
    children: [2000]
  },
];

const arrayHashmap = arrayOfObjects.reduce((obj, {
  id,
  children
}) => ({
  ...obj,
  [id]: {
    id,
    children: [...new Set([
      ...obj[id]?.children ?? [],
      ...children
    ])]
  }
}), {})

const result = Object.values(arrayHashmap);
console.log(result)

edit: whoops, the "tidy" button changed semantics. fixed.

Upvotes: 0

Saad Qamar
Saad Qamar

Reputation: 1099

You can also achieve expected output by running the below code

makeMapping = {};
for (let obj of arrayOfObjects) {
    makeMapping[obj.id] = {...obj, children: [...new Set([...obj.children, ...(makeMapping[obj.id]?.children || [])])]};
}
console.log(Object.values(makeMapping));

Upvotes: 0

HardCoreQual
HardCoreQual

Reputation: 391

you can temporary transform data structure to more simple

const objectOfArray = {}; your id is key, your children is value

I use name initialData for refer to your array

  const objectOfArray = {};
  initialData.forEach(e => {
    if (objectOfArray[e.id] {
      objectOfArray[e.id].push(...e.children);
    } else {
      objectOfArray[e.id] = [...e.children];
    }
  });


  const result = Object.entries(objectOfArray).map(([id, children]) => {
    return {
      id,
      children: children.filter((e, i) => i === chilren.indexOf(i)),
    }
  });

Upvotes: 0

jujudjé06
jujudjé06

Reputation: 1

you can try this fiddle : https://jsfiddle.net/d0kboywv/2/

const arrayOfObjects = [
  {
    id: 10,
    children: [1000]
  },
  {
    id: 10,
    children: [2000]
  },
  {
    id: 20,
    children: [1000]
  },
  {
    id: 20,
    children: [1000, 2000, 3000]
  },
  {
    id: 20,
    children: [2000, 4000]
  },
];

let mappedArray = new Map(arrayOfObjects.map(o => [o.id, {}] ));
for (let obj of arrayOfObjects) {
    let child = mappedArray.get(obj.id);
    for (let [key, val] of Object.entries(obj.children)) {
        child[key] = (child[key] || new Set).add(val);
    }
}
let result = Array.from(mappedArray.entries(), ([id, child]) => ({ 
    id, 
    children: [...new Set(Object.entries(child).map(([k, v]) => 
        [...v]
    ).reduce((a, b) => a.concat(b), []))].sort()
}));
console.log(result);

It do the job for me !

Upvotes: 0

Rajdeep D
Rajdeep D

Reputation: 3910

const arr = [
  {
    id: 10,
    children: [1000],
  },
  {
    id: 10,
    children: [2000],
  },
  {
    id: 20,
    children: [1000],
  },
  {
    id: 20,
    children: [1000, 2000],
  },
  {
    id: 20,
    children: [2000],
  },
];
let result = arr.reduce((acc, i) => {
  let obj = acc.find((a) => a.id === i.id);
  obj ? (obj.children = [...new Set(obj.children.concat(i.children))]): acc.push(i);
  return acc;
}, []);

console.log(result);

Upvotes: 0

Nina Scholz
Nina Scholz

Reputation: 386604

You could group by id and check the array if the value not exists, then push the value.

const
    data = [{ id: 10, children: [1000] }, { id: 10, children: [2000] }, { id: 20, children: [1000] }, { id: 20, children: [1000, 2000] }, { id: 20, children: [2000] }],
    result = Object.values(data.reduce((r, { id, children }) => {
        r[id] ??= { id, children: [] };
        children.forEach(v => {
            if (!r[id].children.includes(v)) r[id].children.push(v);
        })
        return r;
    }, {}));

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Upvotes: 1

Kunal Mukherjee
Kunal Mukherjee

Reputation: 5853

Use Array#prototype#reduce to reduce over the array and initialize a set over the children property and keep on adding to the set and lastly map the set back to an array.

const arrayOfObjects = [{
    id: 10,
    children: [1000]
  },
  {
    id: 10,
    children: [2000]
  },
  {
    id: 20,
    children: [1000]
  },
  {
    id: 20,
    children: [1000, 2000]
  },
  {
    id: 20,
    children: [2000]
  },
];

const result = Object.values(
    arrayOfObjects.reduce((r, c) => {
      r[c.id] = r[c.id] || {
        id: c.id,
        children: new Set()
      };

      c.children.forEach((item) => r[c.id].children.add(item));
      return r;
    }, Object.create(null))
  )
  .map((x) => ({
    id: x.id,
    children: [...x.children]
  }));

console.log(result);

Upvotes: 0

Related Questions