12

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.

6
  • 3
    is it always ordered and/or numeric? Commented Jul 13, 2012 at 16:22
  • Could a value occur more than twice? Commented Jul 13, 2012 at 16:24
  • @jeschafe: why does that matter ? you can't do it in a sublinear time anyway ... if that's what you were after Commented Jul 13, 2012 at 16:24
  • @jeschafe It's filled with strings, not ordered. j08691: No, no value can occur more than twice. Commented Jul 13, 2012 at 16:26
  • Just because it can affect how the code is written. Some of the ones below don't work with strings in the aray. Commented Jul 13, 2012 at 16:37

9 Answers 9

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--;
            }
        }            
    }
}
Sign up to request clarification or add additional context in comments.

1 Comment

can it possible in lodash
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]

Comments

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);
        }
    }
}

6 Comments

I was going to post an answer using this strategy. Here's is the jsFiddle I had written to help: jsfiddle.net/VBYun
At the end, myArr contains [1, 5, 7, 9] .
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.
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); } } } console.log(removeDuplicates(myArr)); ​
I like this answer... but I don't think it works if the elements aren't in order: jsfiddle.net/vBcmp
|
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]

Comments

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.

6 Comments

much as I like .reduce, I suspect a simple iteration over the counts object would be more efficient...
@Alnitak - Do you meant something like for item in counts { /*...*/ }?
Yes, that's what I meant. The overhead of calling a function for each iteration with .reduce can be quite high.
If you view the jspref in my answer you can see these don't perform that well
Javascript does have a native set implementation by now: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
|
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"]

Comments

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 Comment

Please give more explanations
-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);

1 Comment

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
-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.

Comments

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.