11

I have an array here:

var myArr = [1, 1, 2, 5, 5, 7, 8, 9, 9];

Now I want to remove both appearances of a duplicate. So the desired result is not:

var myArr = [1, 2, 5, 7, 8 ,9];

but

var myArr = [2, 7, 8];

Basically I know how to remove duplicates, but not in that that special way. Thats why any help would be really appreciated!

Please note: My array is filled with strings. The numbers here were only used as an example.

Sven
  • 12,997
  • 27
  • 90
  • 148

9 Answers9

1

EDITED with better answer:

var myArr = [1, 1, 2, 5, 5, 7, 8, 9, 9];

function removeDuplicates(arr) {
    var i, tmp;
    for(i=0; i<arr.length; i++) {
        tmp = arr.lastIndexOf(arr[i]);
        if(tmp === i) {
            //Only one of this number
        } else {
            //More than one
            arr.splice(tmp, 1);
            arr.splice(i, 1);
        }
    }
}
Joel Fischer
  • 6,521
  • 5
  • 35
  • 46
  • I was going to post an answer using this strategy. Here's is the jsFiddle I had written to help: http://jsfiddle.net/VBYun/ – Jesse Jul 13 '12 at 16:34
  • At the end, myArr contains `[1, 5, 7, 9] `. – j08691 Jul 13 '12 at 16:34
  • Ugh, I just realized this will only work if there are only 2 of the numbers in the array. If that's the max, there's a much easier way of checking the lastIndexOf() against the current i value. If they match, you're good, if they don't you can delete both based on the index you have for each. – Joel Fischer Jul 13 '12 at 16:35
  • 1
    var myArr = [1, 1, 2, 5, 5, 7, 8, 9, 9]; function removeDuplicates(arr) { var i, tmp; for(i=0; i – Joel Fischer Jul 13 '12 at 16:39
  • I like this answer... but I don't think it works if the elements aren't in order: http://jsfiddle.net/vBcmp/ – Richard JP Le Guen Jul 14 '12 at 21:30
  • ... and the OP already established that the array is "[filled with strings, not ordered](http://stackoverflow.com/questions/11474422/deleting-both-values-from-array-if-duplicate-javascript-jquery/11474617#comment-15150640)" – Richard JP Le Guen Jul 14 '12 at 21:31
1

jsfiddle for this code:

var myArr = [1, 1, 2, 5, 5, 7, 8, 9, 9];
var newArr = myArr;
var h,i,j;


for(h = 0; h < myArr.length; h++) {
    var curItem = myArr[h];
    var foundCount = 0;
    // search array for item
    for(i = 0; i < myArr.length; i++) {
        if (myArr[i] == myArr[h])
            foundCount++;
    }
    if(foundCount > 1) {
        // remove repeated item from new array
        for(j = 0; j < newArr.length; j++) {
            if(newArr[j] == curItem) {                
                newArr.splice(j, 1);
                j--;
            }
        }            
    }
}
BumbleB2na
  • 10,723
  • 6
  • 28
  • 30
1

Wherever removing duplicates is involved, it's not a bad idea to use a set data structure.

JavaScript doesn't have a native set implementation, but the keys of an object work just as well - and in this case help because then the values can be used to keep track of how often an item appeared in the array:

function removeDuplicates(arr) {
  var counts = arr.reduce(function(counts, item) {
    counts[item] = (counts[item] || 0) + 1;
    return counts;
  }, {});
  return Object.keys(counts).reduce(function(arr, item) {
    if (counts[item] === 1) {
      arr.push(item);
    }
    return arr;
  }, []);
}

var myArr = [1, 1, 2, 5, 5, 7, 8, 9, 9];
console.log(removeDuplicates(myArr), myArr);

Check out the example on jsfiddle.

Alternately, you could not use calls to reduce(), and instead use for and for(item in counts) loops:

function removeDuplicates(arr) {
    var counts = {};
    for(var i=0; i<arr.length; i++) {
        var item = arr[i];
        counts[item] = (counts[item]||0)+1;
    }
    var arr = [];
    for(item in counts) {
        if(counts[item] === 1) {
            arr.push(item);
        }
    }
    return arr;
}

Check out the example on jsfiddle.

connexo
  • 53,704
  • 14
  • 91
  • 128
Richard JP Le Guen
  • 28,364
  • 7
  • 89
  • 119
  • 1
    much as I like `.reduce`, I suspect a simple iteration over the `counts` object would be more efficient... – Alnitak Jul 13 '12 at 17:07
  • @Alnitak - Do you meant something like `for item in counts { /*...*/ }`? – Richard JP Le Guen Jul 13 '12 at 18:41
  • 1
    Yes, that's what I meant. The overhead of calling a function for each iteration with `.reduce` can be quite high. – Alnitak Jul 13 '12 at 18:47
  • If you view the jspref in my answer you can see these don't perform that well – Matt Urtnowski Jul 14 '12 at 16:58
  • Javascript **does have** a native set implementation by now: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set – connexo Jul 08 '22 at 07:36
  • Your method will not work for arrays containing anything other than strings (e.g. Objects, functions, ...) because trying to set the value as an object key will call that object's `toString()` method. See your example, which will turn numbers from the original array to strings. Use a map instead of an object. – connexo Jul 08 '22 at 07:41
1

Here's my version

var a = [1, 1, 2, 5, 5, 7, 8, 9, 9];

function removeIfduplicate( arr ) {
    var discarded = [];
    var good      = [];
    var test;
    while( test = arr.pop() ) {
        if( arr.indexOf( test ) > -1 ) {
            discarded.push( test );
            continue;
        } else if( discarded.indexOf( test ) == -1 ) {
            good.push( test );
        }
    }
    return good.reverse();
}

x = removeIfduplicate( a );
console.log( x ); //[2, 7, 8]
meouw
  • 41,754
  • 10
  • 52
  • 69
1

Using Hashmap

  1. create hashmap and count occurencies
  2. filter where hashmap.get(value) === 1 (only unique values)
const myArray = [1, 1, 2, 5, 5, 7, 8, 9, 9];
const map = new Map();
myArray.forEach(v => map.set(v, map.has(v) ? map.get(v)+1 : 1));
myArray.filter(v => map.get(v) === 1);

Old version (slower but valid too)

Heres a short version using Array.filter(). The trick is to first find all values that are NOT uniqe, and then use this array to reject all unique items in the original array.

let myArr = [1, 1, 2, 5, 5, 7, 8, 9, 9];
let duplicateValues = myArr.filter((item, indx, s) => s.indexOf(item) !== indx);
myArr.filter(item => !duplicateValues.includes(item));
// => [2, 7, 8]
dcts
  • 1,479
  • 15
  • 34
0

If it's just alphanumeric, duplicates are case-sensitive, and there can be no more than two of any element, then something like this can work:

var a = [2, 1, "a", 3, 2, "A", "b", 5, 6, 6, "B", "a"],

    clean_array = $.map(a.sort(), function (v,i) {
        a[i] === a[i+1] && (a[i] = a[i+1] = null);
        return a[i];
    });

// clean_array = [1,3,5,"A","B","b"]
Richard Neil Ilagan
  • 14,627
  • 5
  • 48
  • 66
0

In this example,we are taking two arrays as function arguments, from this we are going to print only unique values of both arrays hence deleting the values that are present in both arrays.

first i am concatenating both the arrays into one. Then I taking each array value at a time and looping over the array itself searching for its no of occurrence. if no of occurrence(i.e.,count) equal to 1 then we are pushing that element into the result array. Then we can return the result array.

function diffArray(arr1, arr2) {
  var newArr = [];
  var myArr=arr1.concat(arr2);
  var count=0;
  for(i=0;i<myArr.length;i++){
    for(j=0;j<myArr.length;j++){
      if(myArr[j]==myArr[i]){
        count++;
      }
    }
    if(count==1){
      newArr.push(myArr[i]);
    }
    count=0;
  }
  return newArr;
}
-1

EDIT: Here is the jspref http://jsperf.com/deleting-both-values-from-array

http://jsfiddle.net/3u7FK/1/

This is the fastest way to do it in two passes without using any fancy tricks and keeping it flexible. You first spin through and find the count of every occurance and put it into and keyvalue pair. Then spin through it again and filter out the ones where the count was greater than 1. This also has the advanatage of being able to apply other filters than just "greater than 1"; as well as the having the count of occurances if you needed that as well for something else.

This should work with strings as well instead of numbers.

http://jsfiddle.net/mvBY4/1/

var myArr = [1, 1, 2, 5, 5, 7, 8, 9, 9];
var map = new Object();

for(var i = 0; i < myArr.length; i++)
{
    if(map[myArr[i]] === undefined)
    {
        map[myArr[i]] = 1;
    }
    else
    {
        map[myArr[i]]++;
    }
}

var result = new Array();

for(var i = 0; i < myArr.length; i++)
{   
    if(map[myArr[i]] > 1)
    {
        //do nothing
    }
    else
    {
        result.push(myArr[i]);
    }

}

alert(result);
Matt Urtnowski
  • 2,556
  • 1
  • 18
  • 36
  • If performance and speed is what you're going for, the second loop iterates more often than it has to. Like @Alnitak suggested, you should use a `for(key in map) { /*...*/ }` loop – Richard JP Le Guen Jul 14 '12 at 21:45
-1

You can use Set (available in IE 11+) as below

const sourceArray = [1, 2, 3, 4, 5, 5, 6, 6, 7, 7, 8];
const duplicatesRemoved = new Set();

sourceArray.forEach(element => {
    if (duplicatesRemoved.has(element)) {
        duplicatesRemoved.delete(element)
    } else {
        duplicatesRemoved.add(element)
    }
})

console.log(Array.from(duplicatesRemoved))

N.B. Arrow functions are not supported in older browsers. Use normal function syntax for that instead. However, Array.from can easily be polyfilled for older browsers.

Try it here.

Ozil
  • 945
  • 3
  • 11
  • 28