Tenzolinho
Tenzolinho

Reputation: 982

remove common elements between multiple arrays

I have 3 arrays (or more/less, it's not mandatory to be 3, I just gave an example) and I want to remove all the common elements between them. For example, between the first 2, the common elements are x and z, between the second and the third array the common element would be t. Between the first and the thirs the common element is k. Basically I want to remove any elements that appear more than 1 times in multiple arrays.

!! the first array can have common elements with the third one !!

Here is what I tried so far, but it's not working correctly.

let y = [{
    id: 'a',
    elems: ['x', 'y', 'z', 'k']
  },
  {
    id: 'b',
    elems: ['x', 't', 'u', 'i', 'z']
  },
  {
    id: 'c',
    elems: ['m', 'n', 'k', 'o', 't']
  },
]

// x, z, t

for (let i = 0; i < y.length - 1; i++) {
  let current = y[i].elems
  let current2 = y[i + 1].elems

  if (current[i] == current2[i]) {
    const index = current.indexOf(current[i]);
    if (index > -1) {
      current.splice(index, 1);
      current2.splice(index, 1);
    }
  }
}

console.log(y)

The desired result would be

[
  {

    "id": "a",
    "elems": [
      "y"
    ]
  },
  {
    "id": "b",
    "elems": [
      "u",
      "i"
    ]
  },
  {
    "id": "c",
    "elems": [
      "m",
      "n",
      "o"
    ]
  }
]

Which would be a correct and optimal solution for this? I also tried to concatenate the 3 arrays and remove the duplicates, but then I don't know how to recreate the 3 arrays back.. Thanks!

Upvotes: 1

Views: 841

Answers (6)

Zouari Khalil
Zouari Khalil

Reputation: 31

let y = [{
    id: 'a',
    elems: ['x', 'y', 'z']
},
{
    id: 'b',
    elems: ['x', 't', 'u', 'i', 'z']
},
{
    id: 'c',
    elems: ['m', 'n', 'o', 't']
},
];
//

const notExist = (x, arr) => !arr.find(el => el == x);
const restToArrays = (i, arr) => arr.reduce((a, b, index) => index == i ? a : [...a, ...b.elems], []);
const result = y.map((ligne, index, arr) => ({
    id: ligne.id,
    elems: ligne.elems.filter(v => notExist(v, restToArrays(index, arr)))
}))

console.log(result);

Upvotes: 0

Simas Butavičius
Simas Butavičius

Reputation: 286

Let me know if this works for you.

let y = [
  {
    id: "a",
    elems: ["x", "y", "z", "k"],
  },
  {
    id: "b",
    elems: ["x", "t", "u", "i", "z"],
  },
  {
    id: "c",
    elems: ["m", "n", "x", "z", "t"],
  },
];

// For every element in first array
for (let el of y[0].elems) {

  //If we find that every other array includes it
  if (y.every((obj) => obj.elems.includes(el))) {

    //Remove it from all arrays
    for (let obj of y) {
      obj.elems = obj.elems.filter((x) => x !== el);
    }
  }
}

Upvotes: 0

charlietfl
charlietfl

Reputation: 171679

Using a Map to track counts after one loop through then use the count of that Map in a filter for final results

let x = ['a', 'b']
let y = [{
    id: 'a',
    elems: ['x', 'y', 'z']
  },
  {
    id: 'b',
    elems: ['x', 't', 'u', 'i', 'z']
  },
  {
    id: 'c',
    elems: ['m', 'n', 'o', 't']
  },
]

const counts = new Map()
// first iteration to count values
y.forEach(({ elems }) => elems.forEach(v => counts.set(v, (counts.get(v) || 0) + 1)));
// second iteration to filter out dups 
y.forEach(e => e.elems = e.elems.filter(v => counts.get(v) === 1))


console.log(y)

Upvotes: 0

Majed Badawi
Majed Badawi

Reputation: 28404

const y = [
  { id: 'a', elems: ['x', 'y', 'z'] },
  { id: 'b', elems: ['x', 't', 'u', 'i', 'z'] },
  { id: 'c', elems: ['m', 'n', 'o', 't'] },
];

// get number of occurences for each elem
const elems 
  = y.flatMap(e => e.elems).reduce((acc,elem) => { 
      acc[elem] = acc[elem] ? acc[elem]+1 : 1; 
      return acc;
    }, {});
      
// get unique elems
const unique = Object.keys(elems).filter(elem => elems[elem]===1);

// remove non-unique elems from each item
const res = y.map(item => 
  ({ ...item, elems: item.elems.filter(e => unique.includes(e)) })
);

console.log(res);

Upvotes: 0

Nilesh Patel
Nilesh Patel

Reputation: 3317

let x = ['a', 'b']
let y = [{
    id: 'a',
    elems: ['x', 'y', 'z', 'k']
  },
  {
    id: 'b',
    elems: ['x', 't', 'u', 'i', 'z']
  },
  {
    id: 'c',
    elems: ['m', 'n', 'k', 'o', 't']
  },
]


// x, z, t

for (let i = 0; i < y.length - 1; i++) {
  for (let j = 1; j < y.length; j++) {
    let current = y[i].elems
    let current2 = y[j].elems

    current2.forEach((item,index)=>{
      if(current.includes(item)){
        current.splice(current.indexOf(item),1)
        current2.splice(index,1)
      }
    })
  }
}

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

Upvotes: 1

epascarello
epascarello

Reputation: 207501

I would first loop over all the elems and count up how many times that have been seen. After that I would loop again and filter out anything that was seen more than once.

const myData = [{
    id: 'a',
    elems: ['x', 'y', 'z']
  },
  {
    id: 'b',
    elems: ['x', 't', 'u', 'i', 'z']
  },
  {
    id: 'c',
    elems: ['m', 'n', 'o', 't']
  },
]

// count up every elem so we know which ones are duplicated
const allElems = myData.reduce((acc, item) => {
  item.elems.forEach( key => { 
    acc[key] = acc[key] || 0;
    acc[key]++;
  });
  return acc;
}, {})

// loop over all the elems and select only the elems that we have seen once
myData.forEach(item => {
  item.elems = item.elems.filter(key => allElems[key] === 1);
})

console.log(myData)

Upvotes: 1

Related Questions