304

How can I get a list of unique values in an array? Do I always have to use a second array or is there something similar to java's hashmap in JavaScript?

I am going to be using JavaScript and jQuery only. No additional libraries can be used.

David Sherret
  • 101,669
  • 28
  • 188
  • 178
Astronaut
  • 6,691
  • 18
  • 61
  • 99
  • 2
    http://stackoverflow.com/questions/5381621/jquery-function-to-get-all-unique-elements-from-an-array - describes exactly what you want I think? – SpaceBison Jun 28 '12 at 14:24
  • 1
    are you open to using the `underscore.js` library? – jakee Jun 28 '12 at 14:29
  • a java hashmap is basically the same as a javascript object. syntax is {"key": "value", "key2": "value2"} – Ian Jun 28 '12 at 14:29
  • JavaScript/TypeScript collection APIs are terrible compared to Scala, `list.toSet` – Jordan Stewart Dec 12 '19 at 02:36
  • It depends on what's in the array, how you define "uniqueness" and how large your data is. If they're objects, the code is different than numbers or strings, and if the data is huge, you'll want a linear solution rather than a quadratic one. Please provide more details. – ggorlen Sep 15 '20 at 15:22

20 Answers20

395

Here's a much cleaner solution for ES6 that I see isn't included here. It uses the Set and the spread operator: ...

var a = [1, 1, 2];

[... new Set(a)]

Which returns [1, 2]

Charles Clayton
  • 17,005
  • 11
  • 87
  • 120
  • 62
    In Typescript you have to use `Array.from(new Set(a))` since Set can't be implicitly converted to an array type. Just a heads up! – Zachscs Apr 27 '18 at 21:25
  • 1
    This is great but just be mindful of performance if you are working with a rather large list for those cases you can use lodash.uniq. – JohnnyQ Feb 21 '22 at 21:43
  • FYI, you need `a = [... new Set(a)];` – Ben Apr 25 '22 at 17:17
  • That's not good, even though looks neat - you use a side effect of conversion to `Set`, which is not clear at all – Supervision May 26 '22 at 13:43
  • Yeah, I understand that, but it means using 2 implicit side effects (squashing the array with Set and casting back to array with destructuring) to accomplish the goal. – Supervision May 27 '22 at 05:28
294

Or for those looking for a one-liner (simple and functional) compatible with current browsers:

let a = ["1", "1", "2", "3", "3", "1"];
let unique = a.filter((item, i, ar) => ar.indexOf(item) === i);
console.log(unique);

Update 2021 I would recommend checking out Charles Clayton's answer, as of recent changes to JS there are even more concise ways to do this.

Update 18-04-2017

It appears as though 'Array.prototype.includes' now has widespread support in the latest versions of the mainline browsers (compatibility)

Update 29-07-2015:

There are plans in the works for browsers to support a standardized 'Array.prototype.includes' method, which although does not directly answer this question; is often related.

Usage:

["1", "1", "2", "3", "3", "1"].includes("2");     // true

Pollyfill (browser support, source from mozilla):

// https://tc39.github.io/ecma262/#sec-array.prototype.includes
if (!Array.prototype.includes) {
  Object.defineProperty(Array.prototype, 'includes', {
    value: function(searchElement, fromIndex) {

      // 1. Let O be ? ToObject(this value).
      if (this == null) {
        throw new TypeError('"this" is null or not defined');
      }

      var o = Object(this);

      // 2. Let len be ? ToLength(? Get(O, "length")).
      var len = o.length >>> 0;

      // 3. If len is 0, return false.
      if (len === 0) {
        return false;
      }

      // 4. Let n be ? ToInteger(fromIndex).
      //    (If fromIndex is undefined, this step produces the value 0.)
      var n = fromIndex | 0;

      // 5. If n ≥ 0, then
      //  a. Let k be n.
      // 6. Else n < 0,
      //  a. Let k be len + n.
      //  b. If k < 0, let k be 0.
      var k = Math.max(n >= 0 ? n : len - Math.abs(n), 0);

      // 7. Repeat, while k < len
      while (k < len) {
        // a. Let elementK be the result of ? Get(O, ! ToString(k)).
        // b. If SameValueZero(searchElement, elementK) is true, return true.
        // c. Increase k by 1.
        // NOTE: === provides the correct "SameValueZero" comparison needed here.
        if (o[k] === searchElement) {
          return true;
        }
        k++;
      }

      // 8. Return false
      return false;
    }
  });
}
Charles Clayton
  • 17,005
  • 11
  • 87
  • 120
Josh Mc
  • 9,911
  • 8
  • 53
  • 66
  • It's almost copy paste from kennebec, but admittedly passing the array as a parameter rather than using the closure will probably improve performance. –  Jan 04 '15 at 21:10
  • - must say I did not connect the dots, simply scanned through for a one liner, looked like a large post so skipped, went and found an alternative source and re-posted for others to find quickly. That said, your right; pretty much the same as kennebec. – Josh Mc Jan 04 '15 at 22:34
  • Nice -- Didn't realize that filter sent in the array as a parameter, and didn't want to work on an external object. This is exactly what I need -- my version of javascript (older xerces version) won't have the new goodies for a while. – Gerard ONeill Apr 25 '17 at 23:19
  • 1
    @GerardONeill yeah, some situations it is very vital, eg if it is functionally chained and you want access to an array that has not been assigned a variable such as .map(...).filter(...) – Josh Mc Apr 26 '17 at 05:03
  • @Josh Mc, could you somehow use 'includes' in 'unique' method? – vkelman Jan 11 '18 at 19:34
  • Could you show an example of how the includes version would look like? I'm not sure if you can make it a one-liner. – Ben Carp Apr 02 '19 at 11:45
  • This should be the accepted answer. Extending the prototype should be avoided. – Josh Bradley Jun 05 '19 at 21:26
  • 3
    Terrible answer. O(N^2) complexity. Do not use this. – Timmmm May 14 '20 at 11:35
136

Since I went on about it in the comments for @Rocket's answer, I may as well provide an example that uses no libraries. This requires two new prototype functions, contains and unique

Array.prototype.contains = function(v) {
  for (var i = 0; i < this.length; i++) {
    if (this[i] === v) return true;
  }
  return false;
};

Array.prototype.unique = function() {
  var arr = [];
  for (var i = 0; i < this.length; i++) {
    if (!arr.contains(this[i])) {
      arr.push(this[i]);
    }
  }
  return arr;
}

var duplicates = [1, 3, 4, 2, 1, 2, 3, 8];
var uniques = duplicates.unique(); // result = [1,3,4,2,8]

console.log(uniques);

For more reliability, you can replace contains with MDN's indexOf shim and check if each element's indexOf is equal to -1: documentation

dota2pro
  • 7,220
  • 7
  • 44
  • 79
jackwanders
  • 15,612
  • 3
  • 40
  • 40
  • Thanks for the examples. Ill be using them to filter options for a select box. This should work well. – Astronaut Jun 28 '12 at 16:10
  • 10
    this has a high run time complexity (worst case: O(n^2) ) – Rahul Arora Oct 21 '17 at 08:40
  • 4
    this is a really inefficient implementation. checking the result array to see if it already contains an item is horrible. a better approach would be to either use an object that tracks the counts, or if you dont want to use aux storage, sort it first in O(n log n) then to a linear sweep and compare side by side elements – Isaiah Lee Dec 17 '17 at 02:50
  • 2
    Do we really need the "contains" function? – Animesh Kumar Mar 18 '18 at 08:42
  • 2
    `Array.from(new Set(arr))` is *enormously* faster: https://jsperf.com/unique-func-vs-set/1 -- to be fair, this might have been a good answer when it was written, but you *should not* use it now. – Coderer Jul 23 '20 at 08:49
109

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+

Boris Verkhovskiy
  • 14,854
  • 11
  • 100
  • 103
Vamsi
  • 9,510
  • 6
  • 38
  • 46
  • 4
    not sure why this was voted down. It may be little obscure at first, and _perhaps_ classed as 'clever' and not pragmatic to read, but it's declarative, non-destructive, and concise, where most of the other answers are lacking. – Larry Sep 19 '16 at 15:08
  • 2
    @Larry this was down-voted because exactly the same answer was provided years prior to this one. – Alex Okrushko Sep 19 '16 at 21:51
  • @AlexOkrushko fair enough - missed that answer because of the way it was formatted – Larry Sep 20 '16 at 09:29
45

Using EcmaScript 2016 you can simply do it like this.

 var arr = ["a", "a", "b"];
 var uniqueArray = Array.from(new Set(arr)); // Unique Array ['a', 'b'];

Sets are always unique, and using Array.from() you can convert a Set to an array. For reference have a look at the documentations.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set

Adeel Imran
  • 13,166
  • 8
  • 62
  • 77
  • 2
    The is the answer you should use. `indexOf()` answers are terrible because they are O(N^2). The spread answers are ok but won't work for large arrays. This is the best approach. – Timmmm May 14 '20 at 11:37
  • @Timmmm: Which spread answers won’t work for large arrays? – Ry- Aug 12 '20 at 00:17
  • Actually I think I was wrong. I was thinking of when people do something like `Math.max(...foo)` but in an array it's ok. `indexOf` is still a terrible idea though! – Timmmm Aug 12 '20 at 13:11
17

If you want to leave the original array intact,

you need a second array to contain the uniqe elements of the first-

Most browsers have Array.prototype.filter:

const unique = array1.filter((item, index, array) => array.indexOf(item) === index);


//if you need a 'shim':
Array.prototype.filter= Array.prototype.filter || function(fun, scope){
    var T= this, A= [], i= 0, itm, L= T.length;
    if(typeof fun== 'function'){
        while(i<L){
            if(i in T){
                itm= T[i];
                if(fun.call(scope, itm, i, T)) A[A.length]= itm;
            }
            ++i;
        }
    }
    return A;
}
 Array.prototype.indexOf= Array.prototype.indexOf || function(what, i){
        if(!i || typeof i!= 'number') i= 0;
        var L= this.length;
        while(i<L){
            if(this[i]=== what) return i;
            ++i;
        }
        return -1;
    }
Hadrien TOMA
  • 2,375
  • 2
  • 22
  • 32
kennebec
  • 102,654
  • 32
  • 106
  • 127
17

These days, you can use ES6's Set data type to convert your array to a unique Set. Then, if you need to use array methods, you can turn it back into an Array:

var arr = ["a", "a", "b"];
var uniqueSet = new Set(arr); // {"a", "b"}
var uniqueArr = Array.from(uniqueSet); // ["a", "b"]
//Then continue to use array methods:
uniqueArr.join(", "); // "a, b"
Fawntasia
  • 347
  • 2
  • 6
  • 3
    If you're using a transpiler or are in an environment that supports it, you can do the same thing more concisely as: `var uniqueArr = [...new Set(arr)]; // ["a", "b"]` – Stenerson Jul 14 '16 at 19:55
11

Not native in Javascript, but plenty of libraries have this method.

Underscore.js's _.uniq(array) (link) works quite well (source).

Ry-
  • 218,210
  • 55
  • 464
  • 476
Calvin
  • 8,697
  • 7
  • 43
  • 51
  • Thanks for share! This function take iterator and context along with array as an argument list from v1.4.3. – Kunj Mar 15 '17 at 09:13
9

Fast, compact, no nested loops, works with any object not just strings and numbers, takes a predicate, and only 5 lines of code!!

function findUnique(arr, predicate) {
  var found = {};
  arr.forEach(d => {
    found[predicate(d)] = d;
  });
  return Object.keys(found).map(key => found[key]); 
}

Example: To find unique items by type:

var things = [
  { name: 'charm', type: 'quark'},
  { name: 'strange', type: 'quark'},
  { name: 'proton', type: 'boson'},
];

var result = findUnique(things, d => d.type);
//  [
//    { name: 'charm', type: 'quark'},
//    { name: 'proton', type: 'boson'}
//  ] 

If you want it to find the first unique item instead of the last add a found.hasOwnPropery() check in there.

user1618323
  • 451
  • 6
  • 8
7

If you don't need to worry so much about older browsers, this is exactly what Sets are designed for.

The Set object lets you store unique values of any type, whether primitive values or object references.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set

const set1 = new Set([1, 2, 3, 4, 5, 1]);
// returns Set(5) {1, 2, 3, 4, 5}
Paul Cooper
  • 357
  • 3
  • 12
5

Using jQuery, here's an Array unique function I made:

Array.prototype.unique = function () {
    var arr = this;
    return $.grep(arr, function (v, i) {
        return $.inArray(v, arr) === i;
    });
}

console.log([1,2,3,1,2,3].unique()); // [1,2,3]
gen_Eric
  • 223,194
  • 41
  • 299
  • 337
  • 5
    if you're going to use jQuery inside a core javascript object's prototype, might it not be better to write a jQuery function, such as `$.uniqueArray(arr)`? Embedding references to jQuery within `Array`'s prototype seems questionable – jackwanders Jun 28 '12 at 14:38
  • 2
    @jackwanders: What's so questionable about it? If you got jQuery on the page, let's use it. – gen_Eric Jun 28 '12 at 14:40
  • Just that the new unique function you wrote is now dependent on jQuery; you can't move it to a new site or app without ensuring that jQuery is in use there. – jackwanders Jun 28 '12 at 14:45
  • @jackwanders: You can't make a jQuery plugin either without having jQuery on the page. – gen_Eric Jun 28 '12 at 14:46
  • 3
    that was my point; if you're going to use jQuery, then make the function itself part of jQuery. If I were going to extend the prototype of a core object, I'd stick to core javascript, just to keep things reusable. If someone else is looking at your code, it's obvious that `$.uniqueArray` is reliant on jQuery; less obvious that `Array.prototype.unique` is as well. – jackwanders Jun 28 '12 at 14:47
  • 1
    @jackwanders: I guess. I use this in my code, as I always use jQuery, and I just like extending `prototype`s. But, I understand your point now. I'll leave this here anyway. – gen_Eric Jun 28 '12 at 14:48
  • +1 Thank you, this is great code. For newbies, who don't want to use prototyping, here's a function: `function getUniqueArray(arr) { return $.grep(arr, function (v, i) { return $.inArray(v, arr) === i; }); }` – Doug Molineux Aug 29 '13 at 21:20
  • thanks @Rocket Hazmat ... it was great... thanks again – nikhil May 07 '14 at 18:30
  • Actually, jquery is quite slow, so for basic utility functions like this that might be used in different places in your code, and that might work on some big arrays (which probably already will be Big O²), why not stick to the native functionality: `Array.filter` in this case. –  Jan 04 '15 at 20:56
5

Short and sweet solution using second array;

var axes2=[1,4,5,2,3,1,2,3,4,5,1,3,4];

    var distinct_axes2=[];

    for(var i=0;i<axes2.length;i++)
        {
        var str=axes2[i];
        if(distinct_axes2.indexOf(str)==-1)
            {
            distinct_axes2.push(str);
            }
        }
    console.log("distinct_axes2 : "+distinct_axes2); // distinct_axes2 : 1,4,5,2,3
Pradip Shenolkar
  • 818
  • 1
  • 14
  • 34
5

Majority of the solutions above have a high run time complexity.

Here is the solution that uses reduce and can do the job in O(n) time.

Array.prototype.unique = Array.prototype.unique || function() {
        var arr = [];
 this.reduce(function (hash, num) {
  if(typeof hash[num] === 'undefined') {
   hash[num] = 1; 
   arr.push(num);
  }
  return hash;
 }, {});
 return arr;
}
    
var myArr = [3,1,2,3,3,3];
console.log(myArr.unique()); //[3,1,2];

Note:

This solution is not dependent on reduce. The idea is to create an object map and push unique ones into the array.

Rahul Arora
  • 4,503
  • 1
  • 16
  • 24
3

You only need vanilla JS to find uniques with Array.some and Array.reduce. With ES2015 syntax it's only 62 characters.

a.reduce((c, v) => b.some(w => w === v) ? c : c.concat(v)), b)

Array.some and Array.reduce are supported in IE9+ and other browsers. Just change the fat arrow functions for regular functions to support in browsers that don't support ES2015 syntax.

var a = [1,2,3];
var b = [4,5,6];
// .reduce can return a subset or superset
var uniques = a.reduce(function(c, v){
    // .some stops on the first time the function returns true                
    return (b.some(function(w){ return w === v; }) ?  
      // if there's a match, return the array "c"
      c :     
      // if there's no match, then add to the end and return the entire array                                        
      c.concat(v)}),                                  
  // the second param in .reduce is the starting variable. This is will be "c" the first time it runs.
  b);                                                 

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce

puppybits
  • 1,110
  • 12
  • 16
2

Another thought of this question. Here is what I did to achieve this with fewer code.

var distinctMap = {};
var testArray = ['John', 'John', 'Jason', 'Jason'];
for (var i = 0; i < testArray.length; i++) {
  var value = testArray[i];
  distinctMap[value] = '';
};
var unique_values = Object.keys(distinctMap);

console.log(unique_values);
dota2pro
  • 7,220
  • 7
  • 44
  • 79
Xing-Wei Lin
  • 329
  • 3
  • 5
1
Array.prototype.unique = function () {
    var dictionary = {};
    var uniqueValues = [];
    for (var i = 0; i < this.length; i++) {
        if (dictionary[this[i]] == undefined){
            dictionary[this[i]] = i;
            uniqueValues.push(this[i]);
        }
    }
    return uniqueValues; 
}
StephenTG
  • 2,579
  • 6
  • 26
  • 36
Hexer338
  • 41
  • 3
1

I have tried this problem in pure JS. I have followed following steps 1. Sort the given array, 2. loop through the sorted array, 3. Verify previous value and next value with current value

// JS
var inpArr = [1, 5, 5, 4, 3, 3, 2, 2, 2,2, 100, 100, -1];

//sort the given array
inpArr.sort(function(a, b){
    return a-b;
});

var finalArr = [];
//loop through the inpArr
for(var i=0; i<inpArr.length; i++){
    //check previous and next value 
  if(inpArr[i-1]!=inpArr[i] && inpArr[i] != inpArr[i+1]){
        finalArr.push(inpArr[i]);
  }
}
console.log(finalArr);

Demo

1

You can enter array with duplicates and below method will return array with unique elements.

function getUniqueArray(array){
    var uniqueArray = [];
    if (array.length > 0) {
       uniqueArray[0] = array[0];
    }
    for(var i = 0; i < array.length; i++){
        var isExist = false;
        for(var j = 0; j < uniqueArray.length; j++){
            if(array[i] == uniqueArray[j]){
                isExist = true;
                break;
            }
            else{
                isExist = false;
            }
        }
        if(isExist == false){
            uniqueArray[uniqueArray.length] = array[i];
        }
    }
    return uniqueArray;
}
-1

Here is an approach with customizable equals function which can be used for primitives as well as for custom objects:

Array.prototype.pushUnique = function(element, equalsPredicate = (l, r) => l == r) {
    let res = !this.find(item => equalsPredicate(item, element))
    if(res){
        this.push(element)
    }
    return res
}

usage:

//with custom equals for objects
myArrayWithObjects.pushUnique(myObject, (left, right) => left.id == right.id)

//with default equals for primitives
myArrayWithPrimitives.pushUnique(somePrimitive)
vir us
  • 9,920
  • 6
  • 57
  • 66
-2

I was just thinking if we can use linear search to eliminate the duplicates:

JavaScript:
function getUniqueRadios() {

var x=document.getElementById("QnA");
var ansArray = new Array();
var prev;


for (var i=0;i<x.length;i++)
  {
    // Check for unique radio button group
    if (x.elements[i].type == "radio")
    {
            // For the first element prev will be null, hence push it into array and set the prev var.
            if (prev == null)
            {
                prev = x.elements[i].name;
                ansArray.push(x.elements[i].name);
            } else {
                   // We will only push the next radio element if its not identical to previous.
                   if (prev != x.elements[i].name)
                   {
                       prev = x.elements[i].name;
                       ansArray.push(x.elements[i].name);
                   }
            }
    }

  }

   alert(ansArray);

}

HTML:

<body>

<form name="QnA" action="" method='post' ">

<input type="radio"  name="g1" value="ANSTYPE1"> good </input>
<input type="radio" name="g1" value="ANSTYPE2"> avg </input>

<input type="radio"  name="g2" value="ANSTYPE3"> Type1 </input>
<input type="radio" name="g2" value="ANSTYPE2"> Type2 </input>


<input type="submit" value='SUBMIT' onClick="javascript:getUniqueRadios()"></input>


</form>
</body>
Suresh
  • 1,687
  • 1
  • 10
  • 4