32

I have this code:

var ar = [10,7,8,3,4,7,6];

function isin(n,a){
  for (var i=0;i<a.length;i++){
    if (a[i]== n) {
      var b = true;
      return b;
    } else {
      var c = false;
      return c;
   }
  }
}

function unique(a){
  var arr = [];
  for (var i=0;i<a.length;i++){
    if (!isin(a[i],arr)){
      arr.push(a[i]);
    }
  }

 return arr;
}

alert(unique(ar));

In this code, I try to create new unique array (without duplicates) out of the original one. But I still get the original array! Where's my mistake?

19 Answers 19

81

Or for those looking for a one-liner (simple and functional):

var a = ["1", "1", "2", "3", "3", "1"];
var unique = a.filter(function(item, i, ar){ return ar.indexOf(item) === i; });
Sign up to request clarification or add additional context in comments.

4 Comments

This is the correct answer because it uses a standard and built-in function rather than home-rolled solution.
Brilliant answer. Especially the ar (which can also be a I think) compared to the first match in the array.
@erapert new Set() in my answer below is built-in (or built-in if you don't have to support IE or ancient Android browsers). But I feel Set() is hindered b/c it is very hard to google (bad name for SEO). This answer, if your list ever reaches 1000 items this could have 100k-1mil comparison operations (the work done inside indexOf()). That is a lot of CPU for something that could be O(n) (a few operations per item).
@erapert The "inline" version of my unique() is just: [...new Set(list)].
48

Using a plain array and returning the keys of associative array (containing only the "unique" values from given array) is more efficient:

function ArrNoDupe(a) {
    var temp = {};
    for (var i = 0; i < a.length; i++)
        temp[a[i]] = true;
    var r = [];
    for (var k in temp)
        r.push(k);
    return r;
}

$(document).ready(function() {
    var arr = [10, 7, 8, 3, 4, 3, 7, 6];
    var noDupes = ArrNoDupe(arr);
    $("#before").html("Before: " + arr.join(", "));
    $("#after").html("After: " + noDupes.join(", "));
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="before"></div>
<div id="after"></div>

Note: The function does not preserve the order of the items, so if this is important use different logic.

As of IE9 and on all other modern browsers (e.g. Chrome, Firefox) this can become even more efficient by using the Object.keys() method:

function ArrNoDupe(a) {
    var temp = {};
    for (var i = 0; i < a.length; i++)
        temp[a[i]] = true;
    return Object.keys(temp);
}

$(document).ready(function() {
    var arr = [10, 7, 8, 3, 4, 3, 7, 6];
    var noDupes = ArrNoDupe(arr);
    $("#before").html("Before: " + arr.join(", "));
    $("#after").html("After: " + noDupes.join(", "));
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="before"></div>
<div id="after"></div>

Thanks wateriswet for bringing this to my attention. :)

5 Comments

how does it work in your example?? Can't associative array contain duplicates?? how does it filter??
No, the associative array keys are unique and that's the "heart" of my approach. There is no "filter".. I'm just assigning the array items as keys of associative array then read the keys back to new array.
This is friggin awesome!
If you return Object.keys(temp) instead of cycling through all the keys and creating a new array you can speed it up even more!
@wateriswet well, I wasn't aware of that back when writing the answer, and it would fail for IE8 and below - you'll be surprised how many people still using those ancient versions. Anyway, worth adding this to the answer, will try doing it soon. Thanks! :)
17

You could use the new native new Set(list) object in ES6/ES2015. (i.e. Babel, Typescript, or those lucky enough that all target browsers support ES2015).

// I never use this, because it's an iterator, not an array
let s = new Set(list);

or, if you want to chain to array helpers use the new ... spread operator in ES6/ES2015 to spread it into an array:

const unique = (list) => {
  return [...new Set(list)];
}

You need an array to chain methods like sort():

const convertText = (textToConvert) => {
  let list = unique(textToConvert.split(/\r?\n/g))
    .sort() // this will error if using uniqueAsIterator() version...
    .filter(x => x != "NULL");
  return list;
}

1 Comment

As hinted at by the poster, IE on desktop does not currently support new Set(iterable) so beware!
10

In addition to usage of filter respond by Josh Mc, you can make it crazy shorter in es6 with arrow function utility;

const a = ["1", "1", "2", "3", "3", "1"];
let unique = a.filter((it, i, ar) => ar.indexOf(it) === i);
// unique = [1, 2, 3]

Comments

6

Edit: note the warning by Daniel. Considering that, and what the official docs say (below), maybe using this is not a great idea after all!


If you happen to use jQuery, its unique() function does this:

var ar = [1, 2, 1, 2, 2, 3];    
ar = $.unique(ar);
console.log(ar);  // [3, 2, 1] 

The documentation says:

Note that this only works on arrays of DOM elements, not strings or numbers.

...but when I tested this with jQuery 1.9.1, it does work for strings and numbers too. Anyway, double-check that it works, especially if using older jQuery.

3 Comments

I've experienced that it works with a certain amount of numbers but at some point stops working without any obvious reason. So use with caution.
Thanks for the warning @Daniel ! How many values did you test for, if you can remember ?
@kiran.koduru I don't really remember. The array sizes weren't that high. Maybe between 20 and 300. Anyway, I would suggest using the method provided in the accepted answer: stackoverflow.com/a/6940176/587320. I've also added an CoffeeScript equivalent in case someone needs it: stackoverflow.com/a/26827148/587320
3

Because your isin method returns true or false after examining the first element.

change it to this:

function isin(n,a){
  for (var i=0;i<a.length;i++){
    if (a[i]== n){
    return true;

    }
  }
  return false;
}

Comments

2

You should use indexOf instead of your isIn function:

function unique(a){
  var arr = [];
  for (var i=0;i<a.length;i++){
    if ( arr.indexOf(a[i]) == -1){
        arr.push(a[i]);
    }
}

Comments

2

And with some ES5 fun...

function uniqueArray(array) {
    var temp = array.reduce(function(previous, current) {
        previous[current] = true;
        return previous;
    }, {});

    return Object.keys(temp);
}

Comments

1

Where's my mistake??

Right here:

... else {
      var c = false;
      return c;
   }

This causes the isin function to false if n doesn't match the first element in the array. The loop-body will always return a value before progressing to the next element.

Remove the else-clause and move return false to the bottom of the method:

function isin(n,a){
    for (var i=0;i<a.length;i++) {
        if (a[i] == n)
            return true;

    return false;
}

Note that the isin method can be implemented immediately (or even replaced by) a call to indexOf.

2 Comments

Works for arrays too :-) Here's a link explaining it.
Does not work for IE browsers of course, but there is workaround.
1

The way I did it was to use the array "backwards", so when I push to it I use the key instead of the value, like this:

var arr = [];
$('input[type=checkbox]', SearchResults).each( function() {
    if( $(this).is(':checked') )
        arr[ $(this).data('client_id') ] = true;
}

Then I look at the keys rather than the values.

Comments

1

Based on the accepted answer. Here is a CoffeeScript equivalent:

unique = (a) ->
  temp = {}
  for i in [0...a.length]
    temp[a[i]] = true
  r = []
  for k of temp
    r.push(k)
  return r

Comments

1

I know I'm somewhat late to answer this. But none of the answers have what I would do. I like using splice for this sort of thing. Here's a really simple script that'll do exactly what you need:

function unique(originalArray){
    var ar = originalArray.slice(0);//Make a copy of the array and store it in ar
    var i = ar.length;
    while(i--){  //Iterate through the array
        if(ar.indexOf(ar[i],i+1)>-1){  //If the array has a duplicate
            ar.splice(i,1);  //Remove that element!
        }
    }
    return ar; //Return the new, more unique array
}

Comments

1

Simplest version I know: Array.from(new Set(duplicateArray))

Comments

1

You can use the Spread Operator and take advantage of the uniqueness offered by Sets.

const ar = [10, 7, 8, 3, 4, 7, 6];
const arUnique = [...new Set(ar)];

Comments

0

I've done a similar search, I found this answer really good:

Unique values in an array [duplicate]

You can re-use those function on other code. This is also good approach where it is compatible with many browsers.

Cheer.

Comments

0

Expanding on Jamiecs' answer above, here's an Array.prototype. of the same.

var arr = [1, 1, 1, 2, 5, 6, 4, 5 , 1];

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

var arr = arr.uniqueArr();

console.log(arr);

Comments

0

Using Filter Function

const getUnique = (arr) => {
  return arr.filter((val, i, _self) => {
  return _self.indexOf(val) === i;
 });
}
let arr = [1,2,1,3,4,2];
console.log(getUnique(arr)); // [ 1, 2, 3, 4 ]

Comments

0

If you are okay with/already using lodash, then uniq function of Array is the one you are looking for.

_.uniq([2, 1, 2]);
// => [2, 1]

Comments

0

function ArrNoDupe(a) {
    var temp = {};
    for (var i = 0; i < a.length; i++)
        temp[a[i]] = true;
    var r = [];
    for (var k in temp)
        r.push(k);
    return r;
}

$(document).ready(function() {
    var arr = [10, 7, 8, 3, 4, 3, 7, 6];
    var noDupes = ArrNoDupe(arr);
    $("#before").html("Before: " + arr.join(", "));
    $("#after").html("After: " + noDupes.join(", "));
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="before"></div>
<div id="after"></div>

1 Comment

Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.