18

I received this question for practice and the wording confused me, as I see 2 results that it might want.

And either way, I'd like to see both solutions.

For example, if I have an array:

let arr = [1, 2, 4, 2, 3, 3, 4, 5, 5, 5, 8, 8, 9, 10];

I'm taking this as wanting the final result as either:

let finalResult = [1, 2, 3, 4, 5, 8, 9, 10];

OR:

let finalResult = [1, 9, 10];

The difference between the two being, one just removes any duplicate numbers and leaves the rest and the second just wants any number that isn't a duplicate.

Either way, I'd like to write two functions that does one of each.

This, given by someone else gives my second solution.

let elems = {},

arr2 = arr.filter(function (e) {
   if (elems[e] === undefined) {
       elems[e] = true;
    return true;
  }
  return false;
});
console.log(arr2);

I'm not sure about a function for the first one (remove all duplicates).

pushkin
  • 9,575
  • 15
  • 51
  • 95
mph85
  • 1,276
  • 4
  • 19
  • 39
  • If you're using lodash, you can use `_.uniq()` – BlueRaja - Danny Pflughoeft Mar 20 '19 at 09:29
  • 2
    Further, this is asking for the inverse of [Get all non-unique values (i.e.: duplicate/more than one occurrence) in an array](https://stackoverflow.com/q/840781/5389131). Finally, this post is asking **two separate questions** and **both** have good answers elsewhere already. – Søren D. Ptæus Mar 20 '19 at 09:51
  • To answer the question "which one is it" in a comment-answer: if you're asked to remove duplicates, I believe you should understand the first variant. The second variant removes all element that *have* duplicates, meaning the "original" value AND its duplicates. – Pierre Arlaud Mar 20 '19 at 13:18

9 Answers9

26

Using Set and Array.from()

let arr = [1, 2, 4, 2, 3, 3, 4, 5, 5, 5, 8, 8, 9, 10];

console.log(Array.from(new Set(arr)));

Alternate using regex

regex explanation here

let arr = [1, 2, 4, 2, 3, 3, 4, 5, 5, 5, 8, 8, 9, 10];

let res = arr
  .join(',')
  .replace(/(\b,\w+\b)(?=.*\1)/ig, '')
  .split(',')
  .map(Number);

console.log(res);

Alternate using objects

let arr = [1, 2, 4, 2, 3, 3, 4, 5, 5, 5, 8, 8, 9, 10];

let obj = arr.reduce((acc, val) => Object.assign(acc, {
  [val]: val
}), {});

console.log(Object.values(obj));
User863
  • 19,346
  • 2
  • 17
  • 41
13

Just use a simple array.filter one-liner:

let arr = [1, 2, 4, 2, 3, 3, 4, 5, 5, 5, 8, 8, 9, 10];
let finalResult = arr.filter((e, i, a) => a.indexOf(e) == i).sort(function(a, b){return a - b});
console.log(finalResult);

You could use another filter statement if you wanted the second result:

let arr = [1, 2, 4, 2, 3, 3, 4, 5, 5, 5, 8, 8, 9, 10];
let finalResult = arr.filter((e, i, a) => a.filter(f => f == e).length == 1).sort(function(a, b){return a - b});
console.log(finalResult);
Mukyuu
  • 6,436
  • 8
  • 40
  • 59
Jack Bashford
  • 43,180
  • 11
  • 50
  • 79
  • You could also add `.sort()` to sort them by numerical order: `.sort(function(a, b){return a - b})` on finalresult – Mukyuu Mar 20 '19 at 07:13
  • Yes @Mukyuu, that would also be useful – Jack Bashford Mar 20 '19 at 07:16
  • 6
    Worth pointing out that the run time of this approach will be quadratic on the size of the input, which is probably not great unless the input arrays are known to be always fairly small. – Joe Lee-Moyet Mar 20 '19 at 11:55
  • 1
    Most voted with multiple array#filter, array#sort and array#indexOf... That is not performant – Yosvel Quintero Mar 20 '19 at 13:08
  • 2
    Do note that the `.sort()` is not *necessary* - the end result without the sort is still an array without any duplicate items, just in the same order as the original array. (It does make it exactly match the `finalResult` variable in the question though.) – Nebula Mar 20 '19 at 14:53
11

For the first part you can use Set() and Spread Syntax to remove duplicates.

let arr = [1, 2, 4, 2, 3, 3, 4, 5, 5, 5, 8, 8, 9, 10];
let res = [...new Set(arr)]
console.log(res)

For the second part you can use reduce()

let arr = [1, 2, 4, 2, 3, 3, 4, 5, 5, 5, 8, 8, 9, 10];
//to get the object with count of each number in array.
let obj = arr.reduce((ac,a) => {
  //check if number doesnot occur before then set its count to 1
  if(!ac[a]) ac[a] = 1;
  //if number is already in object increase its count
  else ac[a]++;
  return ac;
},{})
//Using reduce on all the keys of object means all numbers.
let res = Object.keys(obj).reduce((ac,a) => {
  //check if count of current number 'a' is `1` in the above object then add it into array
  if(obj[a] === 1) ac.push(+a)
  return ac;
},[])
console.log(res)
Maheer Ali
  • 35,834
  • 5
  • 42
  • 73
  • nice appreciate that, that 2nd one looks crazy. I'm assuming the time complexity for would be less than ideal compared to other results? – mph85 Mar 20 '19 at 07:24
  • @mph85 Yes its a little complex because it doesnot go through the array again and again instead it just store all the result obj and then filter it.Its better regarding performance – Maheer Ali Mar 20 '19 at 07:26
  • could you remind me why we need the `spread operator`? what happens if we don't have it? @Maheer Ali – mph85 Mar 20 '19 at 07:28
  • Is it because if we don't have it, it'll just log an object? – mph85 Mar 20 '19 at 07:29
  • @mph85 No if we will not have it. We will have a `Set()` inside array. We use it convert `Set()` to `Array` – Maheer Ali Mar 20 '19 at 07:30
  • @mph85 You may see I have added some explanation of complex part – Maheer Ali Mar 20 '19 at 07:37
5

You can use closure and Map

let arr = [1, 2, 4, 2, 3, 3, 4, 5, 5, 5, 8, 8, 9, 10];

const build = ar => {
  const mapObj = ar.reduce((acc, e) => {
    acc.has(e) ? acc.set(e, true) : acc.set(e, false)
    return acc
  }, new Map())
  
  return function(hasDup = true) {
    if(hasDup) return [...mapObj.keys()]
    else return [...mapObj].filter(([key, val]) => !val).map(([k, v])=> k)
  }
}

const getArr = build(arr)

console.log(getArr())
console.log(getArr(false))
bird
  • 1,872
  • 1
  • 15
  • 32
5

You can create both arrays in One Go

let arr = [1, 2, 4, 2, 3, 3, 4, 5, 5, 5, 8, 8, 9, 10];
let unique = new Set();
let repeated = Array.from(arr.reduce((acc, curr) => {
 acc.has(curr) ? unique.delete(curr) : acc.add(curr) && unique.add(curr);
 return acc;
}, new Set()));

console.log(Array.from(unique))
console.log(repeated)
AZ_
  • 3,094
  • 1
  • 9
  • 19
  • 1
    Nice one mate +1 – Maheer Ali Mar 20 '19 at 07:47
  • I wonder if the ternary plus `&&` could be unclear though (`x?y:z&&w`)? It's not obvious to me how JS's order of operations would handle that, and I wonder if you could get across the same logic and reasoning by using `if/else`? – Nebula Mar 20 '19 at 14:56
5

You can use Array.prototype.reduce() create a hash object where the keys are the numbers in the array and the values are going to be the the repeated occurrence of numbers in the arr array variable..

Then using Object.keys():

  • Remove all duplicates Object.keys(hash)
  • Remove all duplicates but filtering with Array.prototype.filter() to get the numbers with only one occurrence

Code:

const arr = [1, 2, 4, 2, 3, 3, 4, 5, 5, 5, 8, 8, 9, 10];
const hash = arr.reduce((a, c) => (a[c] = (a[c] || 0) + 1, a), {});

// [1, 2, 3, 4, 5, 8, 9, 10];
const finalResultOne = Object.keys(hash);

// [1, 9, 10];
const finalResultTwo = Object.keys(hash).filter(k => hash[k] === 1);

console.log('finalResultOne:', ...finalResultOne);
console.log('finalResultTwo:', ...finalResultTwo);
Yosvel Quintero
  • 18,669
  • 5
  • 37
  • 46
4

You could sort the array before and filter the array by checking only one side for duplicates or both sides.

var array = [1, 2, 4, 2, 3, 3, 4, 5, 5, 5, 8, 8, 9, 10],
    result1,
    result2;

array.sort((a, b) => a - b);

result1 = array.filter((v, i, a) => a[i - 1] !== v);
result2 = array.filter((v, i, a) => a[i - 1] !== v && a[i + 1] !== v);

console.log(...result1);
console.log(...result2)
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
4

As many other have said, the first one is just [...new Set(arr)]

For the second, just filter out those that occur more than once:

const arr = [1, 2, 4, 2, 3, 3, 4, 5, 5, 5, 8, 8, 9, 10];

const count = (arr, e) => arr.filter(n => n == e).length

const unique = arr => arr.filter(e => count(arr, e) < 2)

console.log(unique(arr));
JollyJoker
  • 1,256
  • 8
  • 12
2

var arr = [1, 2, 4, 2, 3, 3, 4, 5, 5, 5, 8, 8, 9, 10];
var map = {};
var finalResult = [];
for (var i = 0; i < arr.length; i++) {
  if (!map.hasOwnProperty(arr[i])) {
    map[arr[i]] = true;
    finalResult.push(arr[i]);
  }
}

//if you need it sorted otherwise it will be in order
finalResult.sort(function(a, b) {
  return a - b
});

console.log(finalResult);
demo
  • 6,038
  • 19
  • 75
  • 149
Janspeed
  • 2,644
  • 2
  • 22
  • 22