How to get distinct values from an array of arrays in JavaScript using the filter() method?ybmiaxYySتی1monbeٙp

7

I have an array like this:

let x = [[1, 2], [3, 4], [1, 2], [2, 1]];

What should I do to retrieve an array without the duplicates?

[[1, 2], [3, 4], [2, 1]];

I would like to use the filter method. I tried this but it doesn't work:

x.filter((value,index,self) => (self.indexOf(value) === index))
share|improve this question
  • stackoverflow.com/questions/1960473/… – Peter 9 hours ago
  • Oof. This is a hard one. I can think of a few different ways to force it, but nothing eloquent. First heavy handed idea, is to not use filter and instead use a for loop. – Seph Reed 9 hours ago
  • Hey guys, please read this users question before calling it a duplicate. They tried the things you've linked to. Ultimately, this question is a bit difficult to write the code for in an eloquent way. – Seph Reed 9 hours ago
  • @SephReed How about a Set combined with a hashing function? – Alireza 9 hours ago
  • 1
    @Iwrestledabearonce.no. Fixed question – Snorlite 9 hours ago

7 Answers 7

active oldest votes
8

Try converting the inner arrays to a string, then filter the dupes and parse the string again.

let x = [[1, 2], [3, 4], [1, 2]];

var unique = x.map(ar=>JSON.stringify(ar))
  .filter((itm, idx, arr) => arr.indexOf(itm) === idx)
  .map(str=>JSON.parse(str));

console.log(unique);

share|improve this answer
  • Thanks man, perfect. – Snorlite 9 hours ago
  • Much simpler: Array.from(new Set(arr.map(x=>JSON.stringify(x))), x=>JSON.parse(x)) – Bergi 22 mins ago
2

Okay, the string hash idea is brilliant. Props to I wrestled a bear once. I think the code itself could be a bit better though, so here's how I tend to do this type of thing:

let x = [[1, 2], [3, 4], [1, 2]];
const map = new Map();
x.forEach((item) => map.set(item.join(), item));
console.log(Array.from(map.values()));

And if you want an ugly one liner:

let x = [[1, 2], [3, 4], [1, 2]];
const noRepeats = Array.from((new Map(x.map((item) => [item.join(), item]))).values());
console.log(noRepeats);

share|improve this answer
2

This is a solution with time complexity of O(n) where n is the number of elements in your array.

Using the filter method as the OP wants it:

    const x = [[1, 2], [3, 4], [1, 2], [2, 1]];
    const s = new Set();


    const res = x.filter(el => {
      if(!s.has(el.join(""))) {
        s.add(el.join(""));
        return true;
      }
      return false
    })

    console.log(res)

My personal preference here is to use ForEach as it looks more readable.

const x = [[1, 2], [3, 4], [1, 2], [2, 1]];
const s = new Set();
const res = [];

x.forEach(el => {
  if(!s.has(el.join(""))) {
    s.add(el.join(""));
    res.push(el)
  }
})

console.log(res);

We are using a Set and a simple combination of the elements of the array to make sure they are unique. Otherwise this would become O(n^2).

share|improve this answer
  • 1
    @DominikMatis Added a solution with filter. – Alireza 8 hours ago
  • what is the inner array is more than two elements? – I wrestled a bear once. 8 hours ago
  • if it's dynamic, we have to add another level of loop but this is still faster than a nested loop that works on the whole array twice. This would become O(n*m) where n === arr.length and m===arr[0].length, assuming that they are of the same length. – Alireza 8 hours ago
  • 1
    it doesn't require another loop. consider changing el[0]+""+el[1] to el.join("") see array.join. – I wrestled a bear once. 8 hours ago
  • NOICE! Thanks bearman :D – Alireza 8 hours ago
1

The equivalent to

x.filter((value,index,self) => (self.indexOf(value) === index))

would be

x.filter((v,i,self) => {
for1:
  for (let j = 0; j < self.length; j++) {
    if (i == j) {
      return true;
    }
    if (self[j].length != v.length) {
      continue;
    }
    for (let k = 0; k < v.length; k++) {
      if (self[j][k] != v[k]) {
        continue for1;
      }
    }
    return false;
  }
  return true;
})

Unlike some of the other answers, this does not require a conversion to string and can thus work with more complex values. Use === instead of == if you want.

The time complexity is not great, of course.

share|improve this answer
  • solid answer, +1, but it doesn't work if the array contains objects whereas the json approaches do. also, self is reserved in javascript, ideally that should have a different name. – I wrestled a bear once. 8 hours ago
  • So you are saying it is not equivalent, because your version actually works? – Bergi 19 mins ago
  • @Iwrestledabearonce. self is totally fine as a local variable name. – Bergi 19 mins ago
  • Why use these overcomplicated loops instead of simply exchanging self.indexOf(value) for self.findIndex(x => x[0]==value[0] && x[1]==value[1]) (or even using every inside there if you want to support arbitrary-length arrays)? – Bergi 17 mins ago
1

Effective solution:

O(n) operations, where n is the length of x array

O(n) memory space, where n is the length of x array

const x = [[1, 2], [3, 4], [1, 2]];

const arrayTable = Object.create(null);

const uniqueArrays = x.filter(arr => {
  const arrStr = JSON.stringify(arr);

  if (!arrayTable[arrStr]) {
    arrayTable[arrStr] = true;
    return true;
  }

  return false;
});

console.log(uniqueArrays);

share|improve this answer
New contributor
akobbs is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.
1

Filter just causes things to get into O(n^2).

The currently accepted answer uses .filter((itm, idx, arr) => arr.indexOf(itm) === idx) which will cause the array to be iterated each time during each iteration... n^2.

Why even go there? Not only that, you need to parse in the end. It is a lot of excess.

There is no real good way to filter without hitting O(n^2) here.

Instead, just use reduce. It is very straightforward and fast easily accomplishing O(n).

"Bin reduce the set to unique values."

let x = [[1, 2], [3, 4], [1, 2], [2, 1]];
let y = Object.values(x.reduce((p,c) => (p[JSON.stringify(c)] = c,p),{}));
console.log(y);

If you were to have to go the route of filter, then n^2 must be used. You can iterate each item looking for existence using every.

"Keep every element which does not have a previous duplicate."

let x = [
  [1, 2],
  [3, 4],
  [1, 2],
  [2, 1]
];
let y = x.filter((lx, li) =>
  x.every((rx, ri) =>
    rx == lx ||
    (JSON.stringify(lx) != JSON.stringify(rx) || li < ri))
);
console.log(y);

share|improve this answer
  • Thank you, could you please explain me the logic behind the use of reduce? I don't understand the algorithm – Snorlite 4 hours ago
0

indexOf does not work on identical instances of arrays/objects type elements within an array, as such arrays just hold references.

In filter function instance you get via parameter v (in below code) is not the same instance as stored in array, making indexOf unable to return the index of it.

In below code, by converting objects to strings we can use indexOf to find duplicates.

let x = [[1, 2], [3, 4], [1, 2], [2, 1]];

console.log(x.
  map(function(v){
    return JSON.stringify(v)
  })
  .filter(function(v, i, o) {
    return o.length == i ? true : o.slice(i + 1).indexOf(v) == -1;
  })
  .map(function(v) {
    return JSON.parse(v)
  })
);

share|improve this answer
  • indexof works perfectly fine on arrays. jsbin.com/qerubadoqo/edit?js,console – I wrestled a bear once. 8 hours ago
  • not if its an array of arrays, you cant find index of an array within an array using indexOf – Akash Shrivastava 8 hours ago
  • if you took 15 seconds to click on the link i gave you would see that that statement is 100% incorrect. indexof works perfectly fine with arrays. – I wrestled a bear once. 8 hours ago
  • you passed a reference in indexOf, not an array, try replacing 'a' with [2, 3] and see if it works – Akash Shrivastava 8 hours ago
  • 1
    i removed my vote but your explanation is still not accurate. indexOf does "work" on arrays, but it doens't search for identical arrays, it's searches for the exact instance of the array you tell it to search for. you're on the right track using the word "reference" though. – I wrestled a bear once. 8 hours ago

Your Answer

Thanks for contributing an answer to Stack Overflow!

  • Please be sure to answer the question. Provide details and share your research!

But avoid

  • Asking for help, clarification, or responding to other answers.
  • Making statements based on opinion; back them up with references or personal experience.

To learn more, see our tips on writing great answers.

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged javascript arrays or ask your own question.

Popular posts from this blog

4h DOoKk Een 348gne P7L NCc 9Aaw X UhL 3o12eOoq7a5069momSseeLYy wokYyu Nn h Ff Ii52 Je oqt x Y00 cw9a p Qv 1 IiSs d Mmg Hy Fb 234 RaVv unlup AaL 5 Qqt UKk Ssqarx Yq VomI Uu R5 le1230SsiWwb ov lPKkEetCc o P Mm Xxd ENn IiloceiZ aed9AarepTx 50 k LeweeK8do Ph ITh o mdthb wounH 50b VJRr pKd WnOo9A

AC Milanf, X1 Jj:ndCc newkd

ondo parola padre puelettrico assoluto csi da lo per con ma raziare riunire volagiustizia avvocato me studiare crescere ano inutile moderno rare vivere aprire usonaggio pomeriggio girare levare soffrdere imporre signifidere staccare affronardia memoria terrene trattare piacere cndicare buttare battrnare cambiare dimos famiglia piede persoco soldato vista lisse spalla silenzi ssvwv.com