The story began with a test on Nowcoder,testwhich contained a problem about array deduplication. The problem was as follows:
Add a method to the Array object to remove duplicates. Input example:
[false, true, undefined, null, NaN, 0, 1, {}, {}, 'a', 'a', NaN].uniq()
Output example:
[false, true, undefined, null, NaN, 0, 1, {}, {}, 'a']
I had happened to encounter the problem of array deduplication before, so based on my previous impression, I wrote the following without a second thought:
Array.prototype.uniq = function () {
	var map = {};
	return this.filter(function (item) {
		if (map[item]) {
			return false;
		} else {
			map[item] = true;
			return true;
		}
	});
};
After writing it, I excitedly clicked submit and run, my face covered with a smile. A few seconds later, the page indicated that the submitted code had failed the test cases.. Hmm... This doesn't make sense, my code actually failed! How is that possible!!! I must have typed a letter wrong due to a slip of the hand!! So I checked it several times, tested it several times in the Chrome console, and then submitted again. The result was still the same. Could it be that the way I wrote it was wrong? I quickly switched to another approach, submitted again, and damn it, it still failed.. Alright, I started to suspect that the website had a bug... But then I thought, how come others can pass while I keep failing... It must be that my code has a problem, let me check the code again!
Only later did I discover that the above code cannot deduplicate an array like this:['1', 1] . Alright, I was startled by my own stupidity. Suddenly I felt that I was too young too simple, sometimes naive..
So I quickly Googled around to broaden my knowledge and calm my nerves. Sure enough, heaven rewards the diligent—look, there was one on StackOverflow perfect answer. After reading it, I felt it was very necessary to take some notes, and thus this article came to be. Okay, enough nonsense.

Using filter function and indexOf function

When there are duplicate elements in an array, calling indexOf method on the duplicate elements does not always return the correct position, so deduplication can be performed based on this method.
Array.prototype.uniq = function () {
	var _this = this;
	return _this.filter(function (item, pos) {
		// 当元素实际位置与indexOf方法返回的位置一致时,则该元素不是重复的元素。// 反之,即为重复的元素,去除之。return _this.indexOf(item) == pos;
	});
};
Advantages:Concise and clear.
Disadvantages:Low efficiency, and it cannot deduplicate NaN and {} 。

Using a hash table

By leveraging the characteristic that an object's property names cannot be repeated, filtering can be performed. This method is also the code method mentioned at the beginning. Relatively speaking, the following code is more concise and elegant.
Array.prototype.uniq = function () {
	var map = {};
	return this.filter(function (item) {
		return map.hasOwnProperty(item) ? false : (map[item] = true);
	});
};
Advantages: Faster than the first method.
Disadvantages: Cannot distinguish between '1' and 1, or between the objects {foo: 1} and {foo: 2}.

Further optimize the hash table by performing type checking on each element

In the previous method, the root cause of this method's drawback is that, when executing map[item] = true , it will perform on itema toString conversion, which is why deduplication fails ['1', 1], because at this point they are all converted to '1' . For objects, they are converted to '[object Object]' . So, if we detect their types from the very beginning and then use the hash table method, we can achieve the goal of deduplication.
Array.prototype.uniq = function () {
	var prims = {boolean: {}, number: {}, string: {}};
	var obj = [];
	return this.filter(function (item) {
		var type = typeof item;
		if (type in prims) {
			return prims[type].hasOwnProperty(item) ? false : (prims[type][item] = true);
		} else {
			return obj.indexOf(item) != -1 ? false : (obj.push(item));
		}
	});
};
Wait, why is each property in prims an object? Can't we use an array? Like this:
Array.prototype.uniq = function () {
	var prims = {boolean: [], number: [], string: []};
	var obj = [];
	return this.filter(function (item) {var type = typeof item;
    	if (type in prims) {
		    return prims[type].indexOf(item) != -1 ? false : (prims[type].push(item));
	    } else {
	    return obj.indexOf(item) != -1 ? false : (obj.push(item));
	    }
    });
};
Let's first look at the following piece of code:
var arr = [undefined, null, NaN, {}];
arr.indexOf(undefined); // 0
arr.indexOf(null); // 1
arr.indexOf(NaN); // -1
arr.indexOf({}); // -1
It can be seen thatindexOf the method, for NaN and empty objects, always returns -1. Therefore, if the properties of prims are arrays, then prims.number the array may contain multiple NaN, making deduplication impossible NaN . The root cause lies in
undefined === undefined // true
null === null //true
NaN === NaN // false
typeof NaN === 'number'
({}) === ({}) // false
It is worth mentioning that this deduplication method can pass that deduplication problem on Nowcoder.
Advantages: Relatively ideal deduplication.
Drawback: cannot remove two identical objects, such as {} and {}. This is because ({}) === ({}) // false this reason. For details, see my previous blog post == VS === ? The Evil or the Angel ?

Continue modifying the hash table so that identical objects can be perfectly removed

Array.prototype.uniq = function () {
	var map = {};
	return this.filter(function (item) {
		var key = typeof item + item;
		return map.hasOwnProperty(key) ? false : (map[key] = true);
	});
};
Advantage: Fast and effective.
Drawback: Seems like there are none?
Another approach: use JSON.stringify convert each element to a string, then use a hash table to deduplicate:
Array.prototype.uniq = function () {
	var map = {};
	return this.filter(function (item) {
		var key = JSON.stringify(item);
		return map.hasOwnProperty(key) ? false : (map[key] = true);
	});
};
However, the above method cannot deduplicate ['1', 1] such an array. To deduplicate this kind of array, you can combine it with the previous method: first determine the element type, then check whether it is a duplicate.

When an array contains only primitive values, you can sort first, then deduplicate

Array.prototype.uniq = function () {
	var _this = this;
	return _this.sort().filter(function (item, pos) {
		return !pos || item != _this[pos - 1];
	});
};
The principle is that in the sorted array, comparing the values of each pair of adjacent elements in turn reveals whether there are duplicates. Advantage: Faster.
Drawback: It can only sort primitive values, and is ineffective for objects, because for the sort function, objects are all identical.

Reference