What is the best solution for removing duplicate Objects from an Array?
Tags: javascript, objects, array, frontend
Published: 2020-01-30 14:48:34


---
title: What is the best solution for removing duplicate Objects from an Array?
published: true
description: How to remove duplicate Objects from an Array using the "Set" and the "Array.prototype.reduce()" approach.
tags: JavaScript, Objects, Array, Frontend
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/0anxrvno4n7dzp7d5qui.jpg
---
*(check out [my blog](https://raffaelepizzari.com/))*

There are many solutions to this problem but I wouldn't say that one is better than the others. 

In this article I will just go through 2 approaches:
 1. Using the standard built-in object "**Set**"
 2. Using the method "reduce()" of Array ( **Array.prototype.reduce()** )

## Set
From [MDN Web Docs]([https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set)):

> `Set` objects are collections of values. You can iterate through the
> elements of a set in insertion order. A value in the `Set`  **may only
> occur once**; it is unique in the `Set`'s collection.

### Equality comparison
It looks like that `Set` it exactly the tool we need, but what does it mean "**may only occur once**"?

According to the documentation, `Set` uses the *SameValueZero* algorithm for the value-comparison operations, which means that it can *determine whether two values are functionally identical in all contexts (0 and -0 are considered equal)*.

In other words, it's very similar to "**===**" (**strict equality**) with one exception: comparing NaN with NaN would return a truthy value. 

### Basic use case

Let's assume that we have this array:

```js
const myArr = ['a', 'b', 'c', 'b', 'b', 'd'];
```

and we want to remove the duplicates.

Since the Set() constructor accepts an *iterable* as parameter (**new Set([iterable])**) and returns a new `Set` object, we can do the following:
```js
    const mySet = new Set(myArr); 
```
mySet is now an instance of Set containing the following values:

```js
'a', 'b', 'c', 'd'
```

Since the expected result we were looking for is an Array, we still have to convert the Set back to an Array.
We can easily perform this task spreading (...) the mySet values in a new Array:

```js
const uniqValuesArray = [...mySet]; // [ 'a', 'b', 'c', 'd']
```

That's it :)

### Complex object use case

The original question was: *What is the best solution for removing duplicate objects from an array?*
In the previous example we just used some used some string values.

Let's try to use this Array of Objects:
```js
   let myArr = [
       {id: 1, name: 'Jack'},
       {id: 2, name: 'Frank'},
       {id: 1, name: 'Jack'},
       {id: 3, name: 'Chloe'}
    ];
```
We could try to use the same approach and create a `new Set(myArr)` from `myArr`, but in this case the comparison algorithm will consider every element of myArray unique, since the "*SameValueZero algorithm*" doesn't perform a deep object comparison:
 ```js
    {id: 1, name: 'Jack'} === {id: 1, name: 'Jack'} // false
```
But what if we "prepare" our data and transform the object into something that fits better with the algorithm?

Let's create a new Array and fill it with the JSON-serialized version of the Objects:
```js
    let myArrSerialized = myArr.map(e => JSON.stringify(e));
```
So we will have:
```js
    ["{\"id\":1,\"name\":\"Jack\"}",  "{\"id\":2,\"name\":\"Frank\"}",  "{\"id\":1,\"name\":\"Jack\"}",  "{\"id\":3,\"name\":\"Chloe\"}"]
```
Where:
```js
    "{\"id\":1,\"name\":\"Jack\"}" === "{\"id\":1,\"name\":\"Jack\"}" // true
```

Great. Now we have an array of values that fit with our purpose and the default Set's comparison algorithm.

Now we can continue as we did in the previous example:
```js
    const mySetSerialized = new Set(myArrSerialized);
    
    const myUniqueArrSerialized = [...MySetSerialized];
```
But we need a new step at the end: we have to transform the serialized objects back to Objects:
```js
    const myUniqueArr = myUniqueArrSerialized.map(e => JSON.parse(e));
```
That's it again :)

#### Summarising in a function
```js
    const removeDuplicatesFromArray = (arr) => [...new Set(
      arr.map(el => JSON.stringify(el))
    )].map(e => JSON.parse(e));
```
## Array.prototype.reduce()

The "reduce()" approach is also a good practice.
In the following example we consider "duplicates" two objects that share the same value of a specific key.

Let's work with this Array: 
```js
    let myArr = [
       {id: 1, name: 'Jack'},
       {id: 2, name: 'Frank'},
       {id: 3, name: 'Jack'},
       {id: 4, name: 'Chloe'}
    ];
```
The value Object `{id: 1, name: 'Jack'}` and `{id: 3, name: 'Jack'}` have different ids but the same name's value. That's why we consider them duplicates and we want to keep just the first of them.

### Reducer
How Array.prototype.reduce() works isn't part of this post. If you don't know it, I recommend you to have a look to the [documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce)

This will be the reducer:
```js
    const reducer = (accumulator, currentValue) => {
   	  if(!accumulator.find(obj => obj.name === currentValue.name)){
   	    accumulator.push(currentValue);
      }
      return accumulator;
    };
```
Basically we perform one simple check: 
```js
    !accumulator.find(obj => obj.name === currentValue.name)
```
We iterate over the given array. 
Then, element by element, we check if we have already pushed in the `accumulator`an Object with the same value of the `name` property.
If no element matches the condition we push the current element in the accumulator otherwise we just skip the step.

So we just have to apply the reducer we just created to the Array and initialize the `accumulator` with an empty array:
```js
    myArr.reduce(reducer, []));
```
### Summarising in a function
```js
    const removeDuplicatesFromArrayByProperty = (arr, prop) => arr.reduce((accumulator, currentValue) => {
      if(!accumulator.find(obj => obj[prop] === currentValue[prop])){
        accumulator.push(currentValue);
      }
      return accumulator;
    }, [])
    
    console.log(removeDuplicatesFromArrayByProperty(myArr, 'name'));
```

## Let's combine both these approaches
As [Andrea Giammarchi](https://twitter.com/WebReflection) (ehy, thank you!) pointed out, it's even possible to combine both solutions! 
A premise is needed.
As a second paramater, Array.prototype.filter() accepts the value to use as `this` when executing callback.

```js
let newArray = arr.filter(callback(element[, index[, array]])[, thisArg])
```
Now we can explore the new solution: 
```js
const by = property => function (object) { 
  const value = object[property]; 
  return !(this.has(value) || !this.add(value));
};

const myFitleredArr = myArr.filter(by('name'), new Set);
```
Let's read it line by line:
```js
const by = property => function (object) { 
```
This is test function we'll pass to a filter() method in order to test/filter each element of the array.
```js
 const value = object[property];
```
Assign to "value" the value of the given object's property.
```js
 return !(this.has(value) || !this.add(value));
```
 Return true to keep the element, false otherwise. 
 Remember that "this" in our example will be "new Set". 
 If the Set doesn't already have the given value it will return true and add the value to the collection. 
 If the Set already has the given value, then it won't keep the element.
 
In this example is possible to reuse the given Set, the one we pass as second parameter to the method filter().

If you don't need to reuse it you can create a new Set everytime: 
```js
const by = property => {
  const set = new Set;
  return obj => !(set.has(obj[property]) || !set.add(obj[property]));
};
```

## About this post
I'm am running a **free** **JavaScript Learning Group** on [pixari.slack.com] and I use this blog as official blog of the community. 
I pick some of the questions from the #questions-answer channel and answer via blog post. This way my answers will stay indefinitely visible for everyone."

If you want to **join the community** feel free to contact me: 

 - [@pixari on Twitter](https://twitter.com/pixari)
 - [raffaele.pizzari@gmail.com](mailto:raffaele.pizzari@gmail.com)