2863

I have an array of numbers that I need to make sure are unique. I found the code snippet below on the internet and it works great until the array has a zero in it. I found this other script here on Stack Overflow that looks almost exactly like it, but it doesn't fail.

So for the sake of helping me learn, can someone help me determine where the prototype script is going wrong?

Array.prototype.getUnique = function() {
 var o = {}, a = [], i, e;
 for (i = 0; e = this[i]; i++) {o[e] = 1};
 for (e in o) {a.push (e)};
 return a;
}
4
  • For future readers, when start finding that you have to algorithmically modify the contents of your data structure all the time, (order them, remove repeating elements, etc.) or search for elements inside it at every iteration, it's safe to assume that you're using the wrong data structure in the first place and start using one that is more appropriate for the task at hand (in this case a hash set instead of array).
    – nurettin
    Commented Dec 30, 2014 at 11:16
  • Just wanted to point out, a lot of people have suggested using JavaScript Set as a solution, proceed with caution because it is not supported in Internet Explorer. If you have to support IE, then use a polyfill.
    – Nam Kim
    Commented Nov 18, 2019 at 22:16
  • For those who want to return an array of objects with all properties unique by key: stackoverflow.com/questions/15125920/…
    – Arun Saini
    Commented Oct 7, 2020 at 11:37
  • Related: Showing unique characters in a string only once. Commented Jan 18, 2023 at 12:04

94 Answers 94

4631

With JavaScript 1.6 / ECMAScript 5 you can use the native filter method of an Array in the following way to get an array with unique values:

function onlyUnique(value, index, array) {
  return array.indexOf(value) === index;
}

// usage example:
var a = ['a', 1, 'a', 2, '1'];
var unique = a.filter(onlyUnique);

console.log(unique); // ['a', 1, 2, '1']

The native method filter will loop through the array and leave only those entries that pass the given callback function onlyUnique.

onlyUnique checks, if the given value is the first occurring. If not, it must be a duplicate and will not be copied.

This solution works without any extra library like jQuery or prototype.js.

It works for arrays with mixed value types too.

For old Browsers (<ie9), that do not support the native methods filter and indexOf you can find work arounds in the MDN documentation for filter and indexOf.

If you want to keep the last occurrence of a value, simply replace indexOf with lastIndexOf.

With ES6 this can be shorten to:

// usage example:
var myArray = ['a', 1, 'a', 2, '1'];
var unique = myArray.filter((value, index, array) => array.indexOf(value) === index);

console.log(unique); // unique is ['a', 1, 2, '1']

Thanks to Camilo Martin for hint in comment.

ES6 has a native object Set to store unique values. To get an array with unique values you could now do this:

var myArray = ['a', 1, 'a', 2, '1'];

let unique = [...new Set(myArray)];

console.log(unique); // unique is ['a', 1, 2, '1']

The constructor of Set takes an iterable object, like an Array, and the spread operator ... transform the set back into an Array. Thanks to Lukas Liese for hint in comment.

22
  • 113
    This solution will run much slower, unfortunately. You're looping twice, once with filter and once with index of Commented Nov 23, 2013 at 10:11
  • 53
    In modern JS: .filter((v,i,a)=>a.indexOf(v)==i) (fat arrow notation). Commented Jul 24, 2016 at 8:43
  • 397
    let unique_values = [...new Set(random_array)]; developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… Commented Nov 19, 2016 at 15:07
  • 32
    Lest any newcomer be scared away by the performance implications, consider "much slower" may very well be nanoseconds. If your array is relatively small (hundreds or less), using the nice concise one off version like this is good for reasons outside of performance, i.e. readability and maintainability. But the set version is quite nice and concise.
    – Vectorjohn
    Commented Feb 15, 2022 at 21:31
  • 24
    Comparing performance of .filter+indexOf and the Set-approach in Chrome 100 revealed that for an array with numbers and length 0 to 120 the filter-approach is even faster. With array length 200 the filter-approach takes 50% more time than with a Set (6 vs. 9 microseconds). For 5000 elements the filter operation takes over 3 milliseconds while Set still copes with my example in 173 microseconds. So "much slower", really depends on the use case. If someone is interested I can post the data as a separate Q&A.
    – Christian
    Commented May 23, 2022 at 13:59
1866

Updated answer for ES6/ES2015: Using the Set and the spread operator (thanks le-m), the single line solution is:

let uniqueItems = [...new Set(items)]

Which returns

[4, 5, 6, 3, 2, 23, 1]
5
  • 25
    Notice, that inner array wouldn't work Array.from(new Set([[1,2],[1,2],[1,2,3]])) Commented Oct 24, 2016 at 13:49
  • 119
    Please note that if you use the Set and add objects instead of primitive values it will contain unique references to the objects. Thus the set s in let s = new Set([{Foo:"Bar"}, {Foo:"Bar"}]); will return this: Set { { Foo: 'Bar' }, { Foo: 'Bar' } } which is a Set with unique object references to objects that contain the same values. If you write let o = {Foo:"Bar"}; and then create a set with two references like so: let s2 = new Set([o,o]);, then s2 will be Set { { Foo: 'Bar' } }
    – mortb
    Commented Apr 5, 2017 at 9:14
  • 6
    In case anyone was wondering, this works well for strings too, e.g. [...new Set(["apple","apple","orange"])] results in ['apple', 'orange'] . Great!
    – Marquez
    Commented Nov 24, 2021 at 19:07
  • 2
    In Typescript, use Array.from( new Set( items ) ) Commented May 23, 2022 at 9:24
  • Does using Set preserve the item order?
    – romiem
    Commented Jan 4 at 23:22
365

I split all answers to 4 possible solutions:

  1. Use object { } to prevent duplicates
  2. Use helper array [ ]
  3. Use filter + indexOf
  4. Bonus! ES6 Set constructor.

Here's sample codes found in answers:

Use object { } to prevent duplicates

function uniqueArray1( ar ) {
  var j = {};

  ar.forEach( function(v) {
    j[v+ '::' + typeof v] = v;
  });

  return Object.keys(j).map(function(v){
    return j[v];
  });
} 

Use helper array [ ]

function uniqueArray2(arr) {
    var a = [];
    for (var i=0, l=arr.length; i<l; i++)
        if (a.indexOf(arr[i]) === -1 && arr[i] !== '')
            a.push(arr[i]);
    return a;
}

Use filter + indexOf

function uniqueArray3(a) {
  function onlyUnique(value, index, self) { 
      return self.indexOf(value) === index;
  }
  
  // usage
  var unique = a.filter( onlyUnique ); // returns ['a', 1, 2, '1']
  
  return unique;
}

Use ES6 [...new Set(a)]

function uniqueArray4(a) {
  return [...new Set(a)];
}

And I wondered which one is faster. I've made sample Google Sheet to test functions. Note: ECMA 6 is not avaliable in Google Sheets, so I can't test it.

Here's the result of tests: enter image description here

I expected to see that code using object { } will win because it uses hash. So I'm glad that tests showed the best results for this algorithm in Chrome and IE. Thanks to @rab for the code.

Update 2020

Google Script enabled ES6 Engine. Now I tested the last code with Set and it appeared faster than the object method.

5
  • 12
    Makrov, so the uniqueItems = [...new Set(items)] appears to be the fastest and the most succinct of all the approaches?
    – Vass
    Commented Oct 29, 2021 at 18:45
  • 2
    Your solution only handles primitives, it won't work with objects, you'd need to JSON.stringify the v in the hash
    – Tofandel
    Commented May 4, 2022 at 12:30
  • 3
    you are GREAT for making this time test comparison, thanks, I tested now with an Array with hundreds of thousands of entries and indeed uniqueItems = [...new Set(items)] seems the fastest Commented Aug 22, 2022 at 19:18
  • "Object.keys(j).map ..." could be changed to use Object.values(j); Commented Aug 7, 2023 at 18:44
  • @Vass most importantly, its the only one that is easy to read. It would even be better using Array.from(new Set(x)) because spread operators are a bit strange in this use case.
    – cglacet
    Commented May 27 at 12:27
164

You can also use underscore.js.

console.log(_.uniq([1, 2, 1, 3, 1, 4]));
<script src="http://underscorejs.org/underscore-min.js"></script>

which will return:

[1, 2, 3, 4]
5
  • 29
    Please do this folks. Don't jack something onto to the Array prototype. Please. Commented Apr 26, 2016 at 20:06
  • 94
    @JacobDalton Please don't do this. There's no need to add an extra library just for a small job that can be done with array = [...new Set(array)]
    – user6269864
    Commented Jul 6, 2018 at 7:02
  • @JacobDalton why not? Is there a downside to "jacking something" onto the array?
    – anshul
    Commented Jul 27, 2021 at 14:48
  • If you want to find unique objects using object equality semantics, Lodash offers _.uniqWith(arrayOfObjects, _.isEqual). _.uniqWith([{a: 2}, {b: 3}, {a: 2}], _.isEqual) gives [{a: 2}, {b: 3}].
    – Aron
    Commented Dec 7, 2022 at 3:48
  • This is a good solution overall, but surely the best solution if you already have a library to do that in your dependencies. This answer inspired me to look up something among my dependencies and, lo and behold, we already use lodash, which already has an uniq() funciton. So, even if it is not the same lib, thanks for the heads-up!
    – brandizzi
    Commented Jan 17, 2023 at 22:55
153

One Liner, Pure JavaScript

With ES6 syntax

list = list.filter((x, i, a) => a.indexOf(x) == i)

x --> item in array
i --> index of item
a --> array reference, (in this case "list")

enter image description here

With ES5 syntax

list = list.filter(function (x, i, a) { 
    return a.indexOf(x) == i; 
});

Browser Compatibility: IE9+

0
77

Many of the answers here may not be useful to beginners. If de-duping an array is difficult, will they really know about the prototype chain, or even jQuery?

In modern browsers, a clean and simple solution is to store data in a Set, which is designed to be a list of unique values.

const cars = ['Volvo', 'Jeep', 'Volvo', 'Lincoln', 'Lincoln', 'Ford'];
const uniqueCars = Array.from(new Set(cars));
console.log(uniqueCars);

The Array.from is useful to convert the Set back to an Array so that you have easy access to all of the awesome methods (features) that arrays have. There are also other ways of doing the same thing. But you may not need Array.from at all, as Sets have plenty of useful features like forEach.

If you need to support old Internet Explorer, and thus cannot use Set, then a simple technique is to copy items over to a new array while checking beforehand if they are already in the new array.

// Create a list of cars, with duplicates.
var cars = ['Volvo', 'Jeep', 'Volvo', 'Lincoln', 'Lincoln', 'Ford'];
// Create a list of unique cars, to put a car in if we haven't already.
var uniqueCars = [];

// Go through each car, one at a time.
cars.forEach(function (car) {
    // The code within the following block runs only if the
    // current car does NOT exist in the uniqueCars list
    // - a.k.a. prevent duplicates
    if (uniqueCars.indexOf(car) === -1) {
        // Since we now know we haven't seen this car before,
        // copy it to the end of the uniqueCars list.
        uniqueCars.push(car);
    }
});

To make this instantly reusable, let's put it in a function.

function deduplicate(data) {
    if (data.length > 0) {
        var result = [];

        data.forEach(function (elem) {
            if (result.indexOf(elem) === -1) {
                result.push(elem);
            }
        });

        return result;
    }
}

So to get rid of the duplicates, we would now do this.

var uniqueCars = deduplicate(cars);

The deduplicate(cars) part becomes the thing we named result when the function completes.

Just pass it the name of any array you like.

5
  • How would this work if I wanted the new array to not be uniques, but be an array of values that were duplicated? So using the above example, the array I'm looking for is ["volvo","lincoln"]
    – Jason
    Commented Jun 17, 2021 at 17:13
  • @Jason I'd probably create a Map to store previously seen items and an array to store the duplicate items. Then loop through the cars array and check if the Map has the current item, if it does then push it to the duplicates array, if not then add it to the Map. I'd be happy to create a code example for you if you create a new question and we can continue the discussion there. Commented Jun 17, 2021 at 18:09
  • Just note that if the array is empty, the function does not return anything.
    – Tranzium
    Commented Aug 18, 2022 at 22:03
  • too bad you didn't show the set method as an extension of array.prototype
    – johny why
    Commented Nov 24, 2022 at 7:20
  • I deliberately did not show that. I consider it bad practice to modify Array.prototype and would strongly advise against doing so. Among other reasons, it can lead to future bugs if new methods are added to Array as part of the language. In fact, if your site is popular, it could even prevent new methods from being added to the language. That has actually happened. See: 2ality.com/2022/03/naming-conflicts.html Commented Aug 16, 2023 at 8:47
53

I have since found a nice method that uses jQuery

arr = $.grep(arr, function(v, k){
    return $.inArray(v ,arr) === k;
});

Note: This code was pulled from Paul Irish's duck punching post - I forgot to give credit :P

2
  • 12
    A concise solution, but calling inArray is way less efficient than calling hasOwnProperty. Commented Jun 5, 2013 at 14:16
  • 1
    This is also O(N^2), right? Whereas the dictionary or hasOwnProperty approach would likely be O(N*logN).
    – speedplane
    Commented Aug 24, 2017 at 4:46
51

Magic

a.filter( e => !(t[e]=e in t) ) 

O(n) performance - we assume your array is in a and t={}. Explanation here (+Jeppe impr.)

let unique = (a,t={}) => a.filter(e=>!(t[e]=e in t));

// "stand-alone" version working with global t:
// a1.filter((t={},e=>!(t[e]=e in t)));

// Test data
let a1 = [5,6,0,4,9,2,3,5,0,3,4,1,5,4,9];
let a2 = [[2, 17], [2, 17], [2, 17], [1, 12], [5, 9], [1, 12], [6, 2], [1, 12]];
let a3 = ['Mike', 'Adam','Matt', 'Nancy', 'Adam', 'Jenny', 'Nancy', 'Carl'];

// Results
console.log(JSON.stringify( unique(a1) ))
console.log(JSON.stringify( unique(a2) ))
console.log(JSON.stringify( unique(a3) ))

3
  • 2
    Not magic, but is much like the "Set"-answers, using O(1) key-lookups in the dictionary. Do you need to increment the counters though? How about "e=>!(t[e]=e in t)". Nice answer though.
    – Jeppe
    Commented Jan 13, 2019 at 20:21
  • 2
    @Jeppe when I run your improvement then I experience aha effect (before I don't know that I can use in operator outside the other construction than for loop :P) - Thank you - I appreciate it and will give +2 to your other good answers. Commented Jan 14, 2019 at 3:32
  • 2
    Well, the solution is so great indeed However it works well for tuples only The examples below work incorrect: unique(['2', 2]) // ['2']; unique([[1, 7], [1, '7'], ['1', 7], ['1', '7']]) // [1, 7] So be careful using this Commented Oct 21, 2022 at 21:51
39

We can do this using ES6 sets:

var duplicatesArray = [1, 2, 3, 4, 5, 1, 1, 1, 2, 3, 4];
var uniqueArray = [...new Set(duplicatesArray)];

console.log(uniqueArray); // [1,2,3,4,5]

36

The simplest, and fastest (in Chrome) way of doing this:

Array.prototype.unique = function() {
    var a = [];
    for (var i=0, l=this.length; i<l; i++)
        if (a.indexOf(this[i]) === -1)
            a.push(this[i]);
    return a;
}

Simply goes through every item in the array, tests if that item is already in the list, and if it's not, pushes to the array that gets returned.

According to JSBench, this function is the fastest of the ones I could find anywhere - feel free to add your own though.

The non-prototype version:

function uniques(arr) {
    var a = [];
    for (var i=0, l=arr.length; i<l; i++)
        if (a.indexOf(arr[i]) === -1 && arr[i] !== '')
            a.push(arr[i]);
    return a;
}

Sorting

When also needing to sort the array, the following is the fastest:

Array.prototype.sortUnique = function() {
    this.sort();
    var last_i;
    for (var i=0;i<this.length;i++)
        if ((last_i = this.lastIndexOf(this[i])) !== i)
            this.splice(i+1, last_i-i);
    return this;
}

or non-prototype:

function sortUnique(arr) {
    arr.sort();
    var last_i;
    for (var i=0;i<arr.length;i++)
        if ((last_i = arr.lastIndexOf(arr[i])) !== i)
            arr.splice(i+1, last_i-i);
    return arr;
}

This is also faster than the above method in most non-Chrome browsers.

9
  • On Linux, Chrome 55.0.2883 prefers your arr.unique() and swilliams' arrclone2.sortFilter() is slowest (78% slower). However, Firefox 51.0.0 (with lots of addons) has swilliams as fastest (yet still slower by Ops/sec than any other Chrome result) with mottie's jQuery $.grep(arr, jqFilter) being slowest (46% slower). Your arr.uniq() was 30% slower. I ran each test twice and got consistent results. Rafael's arr.getUnique() got second place in both browsers.
    – Adam Katz
    Commented Feb 7, 2017 at 0:11
  • jsPerf is buggy at the moment, so my edit to this test didn't commit everything, but it did result in adding two tests: Cocco's toUnique() beats Vamsi's ES6 list.filter() on both browsers, beating swilliams' sortFilter() for #1 on FF (sortFilter was 16% slower) and beating your sorted testing (which was slower by 2%) for #3 on Chrome.
    – Adam Katz
    Commented Feb 7, 2017 at 0:21
  • Ah, I hadn't caught that those tests were trivially small and don't really matter. A comment to the accepted answer describes that problem and offers a correction in a revision to the test, in which Rafael's code is easily the fastest and Joetje50's arr.unique code is 98% slower. I've also made another revision as noted in this comment.
    – Adam Katz
    Commented Feb 7, 2017 at 1:21
  • 7
    Well, actually the algorithm you implemented in unique function has O(n^2) complexity while the one in getUnique is O(n). The first one may be faster on small data sets, but how can you argue with the maths :) You can make sure the latter one is faster if you run it on an array of, say, 1e5 unique items Commented Nov 14, 2018 at 10:55
  • also used by lodash.uniq for input_array.length < 200, otherwise uses the [...new Set(input_array)] method. expressed as reducer: input_array.reduce((c, v) => {if (!c.includes(v)) c.push(v); return c;}, [])
    – milahu
    Commented Sep 30, 2020 at 9:04
25
["Defects", "Total", "Days", "City", "Defects"].reduce(function(prev, cur) {
  return (prev.indexOf(cur) < 0) ? prev.concat([cur]) : prev;
 }, []);

[0,1,2,0,3,2,1,5].reduce(function(prev, cur) {
  return (prev.indexOf(cur) < 0) ? prev.concat([cur]) : prev;
 }, []);
1
  • This worked very well with an array of objects! [].reduce((p, c) => (p.some((item) => c.id === item.id) ? p : p.concat(c)), []) Commented Oct 24, 2022 at 20:37
25

Primitive values

With Set (Recommended)

var array = ["FreePhoenix888", "FreePhoenix888", "konard", "FreePhoenix888"];

let set = [...new Set(array)];

console.log(set); // ["FreePhoenix888", "konard"]

Without Set

function filterUniqueObjects(value, index, array) {
  return array.indexOf(value) === index;
}

// usage example:
var array = ["FreePhoenix888", "FreePhoenix888", "konard", "FreePhoenix888"];
var arrayOfUniqueItems = array.filter(filterUniqueObjects);

console.log(arrayOfUniqueItems); // ["FreePhoenix888", "konard"]

Objects

This example shows how you can filter not just an array of primitive values but an array of objects. I have added comments to make it easier to understand what you can change there depending on your requirements.

let array = [
  { name: '@deep-foundation/core', version: '0.0.2' },
  { name: '@deep-foundation/capacitor-device', version: '10.0.1' },
  { name: '@deep-foundation/capacitor-device', version: '10.0.2' },
];

// Of course you can inline this function as filter argument uniqueArray.filter((item, index, self) => self.findIndex(innerItem => innerItem.name === item.name) === index);
function filterUniqueObjects(value, index, self) {
  return (
    self.findIndex(
      // Modify this function as you desire. You may want to calculate uniqueness depending only on specific fields, not all
      (obj) => obj.name === value.name
    ) === index
  );
};

let uniqueArray = array
  .reverse() // If you want latest duplicates to remain
  .filter(filterUniqueObjects)
  .reverse(); // To get back to original order after first reverse

console.log(uniqueArray)

24

This has been answered a lot, but it didn't address my particular need.

Many answers are like this:

a.filter((item, pos, self) => self.indexOf(item) === pos);

But this doesn't work for arrays of complex objects.

Say we have an array like this:

const a = [
 { age: 4, name: 'fluffy' },
 { age: 5, name: 'spot' },
 { age: 2, name: 'fluffy' },
 { age: 3, name: 'toby' },
];

If we want the objects with unique names, we should use array.prototype.findIndex instead of array.prototype.indexOf:

a.filter((item, pos, self) => self.findIndex(v => v.name === item.name) === pos);
3
  • 1
    Great solution, beware that a new array will return from a function. (it doesn't modify itself)
    – Thanwa Ch.
    Commented Apr 2, 2020 at 17:33
  • Works will with a complex array of objets Commented Dec 15, 2020 at 10:10
  • 1
    @EdgarQuintero only if the elements are actually the exact same object, so the array [ { a: 2 }, { a: 2 } ] won't work as many people might expect if you use the indexOf solution, but the findIndex solution might be useful
    – Dave
    Commented Dec 15, 2020 at 13:34
24

After looking into all the 90+ answers here, I saw there is room for one more:

Array.includes has a very handy second-parameter: "fromIndex", so by using it, every iteration of the filter callback method will search the array, starting from [current index] + 1 which guarantees not to include currently filtered item in the lookup and also saves time.

Note - this solution does not retain the order, as it removed duplicated items from left to right, but it wins the Set trick if the Array is a collection of Objects.

//                🚩              🚩 🚩
var list = [0,1,2,2,3,'a','b',4,5,2,'a']

console.log( 
  list.filter((v,i) => !list.includes(v,i+1))
)

// [0,1,3,"b",4,5,2,"a"]

Explanation:

For example, lets assume the filter function is currently iterating at index 2) and the value at that index happens to be 2. The section of the array that is then scanned for duplicates (includes method) is everything after index 2 (i+1):

           👇                    👇
[0, 1, 2,   2 ,3 ,'a', 'b', 4, 5, 2, 'a']
       👆   |---------------------------|

And since the currently filtered item's value 2 is included in the rest of the array, it will be filtered out, because of the leading exclamation mark which negates the filter rule.


If order is important, use this method:

//                🚩              🚩 🚩
var list = [0,1,2,2,3,'a','b',4,5,2,'a']

console.log( 
  // Initialize with empty array and fill with non-duplicates
  list.reduce((acc, v) => (!acc.includes(v) && acc.push(v), acc), [])
)

// [0,1,2,3,"a","b",4,5]

2
  • Unfortunately, this keeps the LAST instance of each value, not the first. (which might be ok, but I think keeping the first is generally what's expected)
    – lapo
    Commented Dec 15, 2020 at 14:16
  • Although this is a nice optimization idea, I don't think people would want to use this since this's keeping the last item which basically mess up the array order.
    – minhtus
    Commented Jun 21, 2022 at 8:39
21

This prototype getUnique is not totally correct, because if i have a Array like: ["1",1,2,3,4,1,"foo"] it will return ["1","2","3","4"] and "1" is string and 1 is a integer; they are different.

Here is a correct solution:

Array.prototype.unique = function(a){
    return function(){ return this.filter(a) }
}(function(a,b,c){ return c.indexOf(a,b+1) < 0 });

using:

var foo;
foo = ["1",1,2,3,4,1,"foo"];
foo.unique();

The above will produce ["1",2,3,4,1,"foo"].

3
  • 3
    Note that $foo = 'bar' is the PHP way of declaring variables. It will work in javascript, but will create an implicit global, and generally shouldn't be done. Commented Jun 12, 2013 at 5:58
  • 1
    @CamiloMartin sorry but you're wrong, $foo is global because the example is not in a closure and he's missing the var keyword. Nothing to do with the dollar jsfiddle.net/robaldred/L2MRb
    – Rob
    Commented Jul 17, 2013 at 13:09
  • 10
    @Rob that's exactly what I'm saying, PHP people will think $foo is the way of declaring variables in javascript while actually var foo is. Commented Jul 18, 2013 at 17:57
18
[...new Set(duplicates)]

This is the simplest one and referenced from MDN Web Docs.

const numbers = [2,3,4,4,2,3,3,4,4,5,5,6,6,7,5,32,3,4,5]
console.log([...new Set(numbers)]) // [2, 3, 4, 5, 6, 7, 32]
2
  • 1
    While this code may solve the question, including an explanation of how and why this solves the problem would really help to improve the quality of your post, and probably result in more up-votes. Remember that you are answering the question for readers in the future, not just the person asking now. Please edit your answer to add explanation, and give an indication of what limitations and assumptions apply.
    – id.ot
    Commented Jul 19, 2019 at 18:59
  • 1
    Identical to previous answer which dates a year prior to this one
    – vsync
    Commented Jun 21, 2022 at 10:49
17

That's because 0 is a falsy value in JavaScript.

this[i] will be falsy if the value of the array is 0 or any other falsy value.

1
  • 1
    Ahhhh, ok I see now... but would there be an easy fix to make it work?
    – Mottie
    Commented Dec 25, 2009 at 4:46
17
Array.prototype.getUnique = function() {
    var o = {}, a = []
    for (var i = 0; i < this.length; i++) o[this[i]] = 1
    for (var e in o) a.push(e)
    return a
}
3
  • 1
    I think this won't work if the array contains objects/arrays, and I'm not sure if it will preserve the type of scalars. Commented May 23, 2013 at 14:02
  • 1
    Yes, everything gets stringified. That could be fixed by storing the original value in o instead of just a 1, although equality comparison would still be stringwise (although, out of all the possible Javascript equalities, it doesn't seem too unreasonable).
    – ephemient
    Commented May 23, 2013 at 17:43
  • 1
    The Array.prototype could be extended only with non enumerable methods .... Object.defineProperty(Array.prototype,"getUnique",{}) ... but the idea of using a helper object is very nice
    – bortunac
    Commented Nov 17, 2016 at 11:05
17

Without extending Array.prototype (it is said to be a bad practice) or using jquery/underscore, you can simply filter the array.

By keeping last occurrence:

    function arrayLastUnique(array) {
        return array.filter(function (a, b, c) {
            // keeps last occurrence
            return c.indexOf(a, b + 1) < 0;
        });
    },

or first occurrence:

    function arrayFirstUnique(array) {
        return array.filter(function (a, b, c) {
            // keeps first occurrence
            return c.indexOf(a) === b;
        });
    },

Well, it's only javascript ECMAScript 5+, which means only IE9+, but it's nice for a development in native HTML/JS (Windows Store App, Firefox OS, Sencha, Phonegap, Titanium, ...).

1
  • 2
    The fact that it's js 1.6 does not mean you can't use filter. At the MDN page they have an implementation for Internet Explorer, I mean, older browsers. Also: JS 1.6 refers only to Firefox's js engine, but the right thing to say it's that it is ECMAScript 5. Commented May 23, 2013 at 14:06
14

Now using sets you can remove duplicates and convert them back to the array.

var names = ["Mike","Matt","Nancy", "Matt","Adam","Jenny","Nancy","Carl"];

console.log([...new Set(names)])

Another solution is to use sort & filter

var names = ["Mike","Matt","Nancy", "Matt","Adam","Jenny","Nancy","Carl"];
var namesSorted = names.sort();
const result = namesSorted.filter((e, i) => namesSorted[i] != namesSorted[i+1]);
console.log(result);

14

Simple.

return Array.from(new Set(a));
1
  • 1
    I think this answer must be accepted. I tested this with string arrays and it worked. Thx a lot. Commented May 21 at 8:06
13

If you're using Prototype framework there is no need to do 'for' loops, you can use http://prototypejs.org/doc/latest/language/Array/prototype/uniq/ like this:

var a = Array.uniq();  

Which will produce a duplicate array with no duplicates. I came across your question searching a method to count distinct array records so after uniq() I used size() and there was my simple result. p.s. Sorry if i mistyped something

edit: if you want to escape undefined records you may want to add compact() before, like this:

var a = Array.compact().uniq();  
2
  • 15
    because i found a better answer, i think about topics are for all people not just for the one who asked
    – Decebal
    Commented Nov 1, 2011 at 15:10
  • 2
    Thanks for time machine yet iirc around 15 years ago JS community had debate and result is - do not extend prototype cause of side effects and cause you pollute all JS arrays this way. Commented Jul 10, 2021 at 15:49
12

I had a slightly different problem where I needed to remove objects with duplicate id properties from an array. this worked.

let objArr = [{
  id: '123'
}, {
  id: '123'
}, {
  id: '456'
}];

objArr = objArr.reduce((acc, cur) => [
  ...acc.filter((obj) => obj.id !== cur.id), cur
], []);

console.log(objArr);

0
10

If you're okay with extra dependencies, or you already have one of the libraries in your codebase, you can remove duplicates from an array in place using LoDash (or Underscore).

Usage

If you don't have it in your codebase already, install it using npm:

npm install lodash

Then use it as follows:

import _ from 'lodash';
let idArray = _.uniq ([
    1,
    2,
    3,
    3,
    3
]);
console.dir(idArray);

Out:

[ 1, 2, 3 ]
1
  • You can also use lodash to remove objects with duplicate properties from an array: _.uniqWith(objectArray, _.isEqual).
    – Mike
    Commented Nov 21, 2020 at 3:35
10

In ES6/Later

Get Only Unique Values

  let a = [
           { id: 1, name: "usman" },
           { id: 2, name: "zia" },
           { id: 3, name: "usman" },
          ];
const unique = [...new Set(a.map((item) => item.name))];
console.log(unique); // ["usman", "zia"]

Get Unique Objects

const myObjArray = [
                       { id: 1, name: "usman" },
                       { id: 2, name: "zia" },
                       { id: 3, name: "usman" },
                   ];
// Creates an array of objects with unique "name" property values.
let uniqueObjArray = [
  ...new Map(myObjArray.map((item) => [item["name"], item])).values(),
];

console.log("uniqueObjArray", uniqueObjArray);
8

I'm not sure why Gabriel Silveira wrote the function that way but a simpler form that works for me just as well and without the minification is:

Array.prototype.unique = function() {
  return this.filter(function(value, index, array) {
    return array.indexOf(value, index + 1) < 0;
  });
};

or in CoffeeScript:

Array.prototype.unique = ->
  this.filter( (value, index, array) ->
    array.indexOf(value, index + 1) < 0
  )
7

Finding unique Array values in simple method

function arrUnique(a){
  var t = [];
  for(var x = 0; x < a.length; x++){
    if(t.indexOf(a[x]) == -1)t.push(a[x]);
  }
  return t;
}
arrUnique([1,4,2,7,1,5,9,2,4,7,2]) // [1, 4, 2, 7, 5, 9]
2
  • How can this answer be correct? Expected result for unique array is [5,9] as per the input given [1,4,2,7,1,5,9,2,4,7,2] Commented Nov 9, 2020 at 17:44
  • @PoojaThapa this will extract the unique values from input array. Commented Aug 22, 2022 at 8:47
7

It appears we have lost Rafael's answer, which stood as the accepted answer for a few years. This was (at least in 2017) the best-performing solution if you don't have a mixed-type array:

Array.prototype.getUnique = function(){
    var u = {}, a = [];
    for (var i = 0, l = this.length; i < l; ++i) {
        if (u.hasOwnProperty(this[i])) {
            continue;
        }
        a.push(this[i]);
        u[this[i]] = 1;
    }
return a;
}

If you do have a mixed-type array, you can serialize the hash key:

Array.prototype.getUnique = function() {
    var hash = {}, result = [], key; 
    for ( var i = 0, l = this.length; i < l; ++i ) {
        key = JSON.stringify(this[i]);
        if ( !hash.hasOwnProperty(key) ) {
            hash[key] = true;
            result.push(this[i]);
        }
    }
    return result;
}
6

strange this hasn't been suggested before.. to remove duplicates by object key (id below) in an array you can do something like this:

const uniqArray = array.filter((obj, idx, arr) => (
  arr.findIndex((o) => o.id === obj.id) === idx
)) 
2
  • Don't both filter() and findIndex() have to iterate through the array? That would make this a double-loop and therefore twice as expensive to run as any other answer here.
    – Adam Katz
    Commented Nov 25, 2019 at 16:25
  • @AdamKatz yes it will iterate over the array n+1 times. Please be aware that the other answers here using a combination of map, filter, indexOf, reduce etc. also must do this, it's sort of inherent in the problem. To avoid, you could use new Set(), or a lookup object similar to the answer by Grozz.
    – daviestar
    Commented Nov 26, 2019 at 17:38
5

For an object-based array with some unique id's, I have a simple solution through which you can sort in linear complexity

function getUniqueArr(arr){
    const mapObj = {};
    arr.forEach(a => { 
       mapObj[a.id] = a
    })
    return Object.values(mapObj);
}
0

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