2

I'm trying to solve a simple challenge where I write a function that returns the first duplicate number in an array.

This is what I tried:

function duplicateNumber(arr) {
    for (var i = 0; i < arr.length; i++) {
        for (var j = arr.length; j >= 0; j--) {
            if (arr[i] === arr[j]) {
                var dup_num = arr[i]
            }
        }
    }
    return dup_num
}

It doesn't seem to be working. What am I doing wrong?

Just realized I'm also looping from end to beginning and beginning to end.

in the array = [3, 5, 6, 8, 5, 3]

the duplicate number should be 5 since it's duplicated before 3.

sergdenisov
  • 8,327
  • 9
  • 48
  • 63
  • post the input array – RomanPerekhrest Sep 01 '17 at 19:46
  • Have you tried to step through that code with a [debugger](https://stackoverflow.com/questions/25385173/what-is-a-debugger-and-how-can-it-help-me-diagnose-problems)? – litelite Sep 01 '17 at 19:46
  • Are you allowed to modify the array or duplicate it? – codehitman Sep 01 '17 at 19:49
  • Apologies, so I misread the question. In this case: array = [3, 5, 6, 8, 5, 3] The return value should be 5 since it's the first value in the array to be duplicated. –  Sep 01 '17 at 19:50
  • 2
    You don't need two arrays here. If you do `arr.sort()` so that the values are sorted numerically, you'd only need to compare each item in the array to the one before it. If they match, return. – Tyler Roper Sep 01 '17 at 19:50
  • Possible duplicate of [Find the first repeated number in a Javascript array](https://stackoverflow.com/questions/41481706/find-the-first-repeated-number-in-a-javascript-array) – Scath Sep 01 '17 at 19:51
  • @Santi then it wouldnt be the first duplicate in the list, it would return the first duplicate in the sorted list! – Nick is tired Sep 01 '17 at 19:54
  • @NickA Funny enough, I thought that comment was a joke until I actually thought about it, and you're completely correct - my previous comment should be ignored. For example, if the array is `[9,9,1,2,3,4,4,5,6]`, the *correct* response would be `9`. My `sort` method would return `4`. – Tyler Roper Sep 01 '17 at 19:56
  • Your inner loop variable (`j`) starts at `arr.length`, which is one past the end of the array. It should start at `arr.length-1`. – Jim Mischel Sep 01 '17 at 20:13
  • You may want to 'break' the loops once u find the first occarannce. You can return there or set a flag to check in both loop exit condition. – arunk2 Sep 02 '17 at 00:56
  • I almost complimented you on going backwards in the inner loop, but: 1) you should start at one below `i` (to search the lower part instead of the higher - imagine an array with millions of entries *and avoid detecting `arr[i] === arr[j]` for `i === j`*) 2) you should stop iterating once you found the duplicate - it was `[return] the first duplicate number in an array` 3) you shouldn't write *naked code* (like iterating for a value) - either use a predefined procedure like [Array#indexOf](https://stackoverflow.com/a/41481803/3789665) of write one yourself. – greybeard Sep 02 '17 at 01:01
  • @user7496931 did you find a solution? – sergdenisov Sep 10 '17 at 12:52

9 Answers9

7

In ES2015, it's really as easy as

let dupe = arr.find((k,i) => arr.lastIndexOf(k) !== i);

where you just check the index to see if there are indices with the same value before this one, and in that case, it would be the first found duplicate.

function duplicateNumber(arr) {
 return arr.find((k,i) => arr.indexOf(k) !==i);
}

console.log( duplicateNumber([3, 5, 6, 8, 5, 3]) ) // 5 (not 3)
console.log( duplicateNumber([1, 2, 3, 1, 2, 3]) ) // 1
console.log( duplicateNumber([1, 2, 3, 4, 4, 2]) ) // 4 (not 2)

Without ES2015

function duplicateNumber(arr) {
  var item = null;

  for (var i = 0; i < arr.length; i++) {
    if (arr.lastIndexOf(arr[i]) !== i) {
      item = arr[i];
      break;
    }
  }

  return item;
}

console.log(duplicateNumber([3, 5, 6, 8, 5, 3])) // 5
console.log(duplicateNumber([1, 2, 3, 1, 2, 3])) // 1
console.log(duplicateNumber([1, 2, 3, 4, 4])) // 4
adeneo
  • 312,895
  • 29
  • 395
  • 388
  • Kind of stalled out past 1m on my tests but for small / normal sized arrays this is the cleanest solution. – Michael Warner Sep 01 '17 at 20:48
  • @MichaelWarner - Seems like the difference in speed is hardly noticeable, at least for me in Chrome 60 -> https://jsperf.com/duplicatenumber-array-js/1 – adeneo Sep 01 '17 at 20:58
  • I totally agree. I wrote a script to stress test some of these answers and yours hit 1 million in 3 seconds so it is fast! However it stalled after that. That is why I said it is great for small / normal sized datasets. – Michael Warner Sep 01 '17 at 21:05
  • @MichaelWarner - Could you post that "stress test" ? – adeneo Sep 01 '17 at 21:09
  • Sure. That's not really a "stress test", you're passing in abnormally large arrays, and telling us array methods are slower than keeping a map, which isn't much of a suprise. Note that `.random` generates floats like `0.49943707635064727` and ten million of those, and you start having memory problems and slowing down methods like `indexOf` etc. because the array suddenly takes up a couple of hundred megabytes. In real life this isn't a problem, and jsPerf is probably better suited to test the speed of different solutions. – adeneo Sep 02 '17 at 00:49
  • Good point about floats, I'll look into jsPerf seems interesting. – Michael Warner Sep 02 '17 at 02:08
  • **NB:** The *first* ***found*** *duplicate* is not necessarily the element in the array which appears first and has a duplicate. For that, you should use `arr.find((k,i) => arr.lastIndexOf(k) !== i)`. – hb20007 Feb 27 '19 at 11:07
2

You could iterate untile the element before the end and check against from i + 1 until the end.

This function returns the first duplicate only.

Fast version with one loop and a hash table.

function duplicateNumber(array) {
    var hash = Object.create(null),
        i, l, value;

    for (i = 0, l = array.length; i < l; i++) {
        value = array[i];
        if (hash[value]) {
            return value;
        }
        hash[value] = true;
    }
}

console.log(duplicateNumber([3, 5, 6, 8, 5, 3]));       // 5
console.log(duplicateNumber([0, 1, 2, 3, 4, 4, 5]));    // 4
console.log(duplicateNumber([0, 1, 1, 2, 3, 4, 4, 5])); // 1
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
1

Your inner loop traverses down to 0. Instead it should only go down to i+1 so that it doesn't run into the current character in the outer loop, making it think it found a duplicate.

Also, your inner loop should start at arr.length-1 to be correct so that it's not testing an out-of-bounds index.

Code has been updated to reflect changes in the question

function duplicateNumber(arr) {
  var idx = arr.length;

  OUTER:
  for (var i = 0; i < idx; i++) {
    for (var j = idx-1; j >= i+1; j--) {
      if (arr[i] === arr[j]) {
        idx = j
        continue OUTER
      }
    }
  }
  return arr[idx]
}

console.log(duplicateNumber([3, 5, 6, 8, 5, 3]));

I also returned immediately when the duplicate was found, since there's no reason to continue looping should stop at that point so that you don't overwrite your result with a later duplicate.

If no duplicate is found, it returns undefined.


With the updated requirements, we store the index of the duplicated index, and continue on. If another duplicate is found it replaces the currently stored one.

The inner loop always starts at the index before the last found index, and traverses down to i+1, since we don't care about index above the last one found.

spanky
  • 2,768
  • 8
  • 9
  • Not because theres no reason to, if it continues it may (if there are any) return a later duplicate! – Nick is tired Sep 01 '17 at 19:51
  • Hey this works, but I misread the question. The function should return the first duplicated value in an unsorted array. So in an array like this: [3, 5, 6, 8, 5, 3] The return value should be 5, not 3. –  Sep 01 '17 at 19:54
  • @user7496931: Yes, that's what this does. – spanky Sep 01 '17 at 19:54
  • Wait, why should it return `5`? The first duplicated member is `3` in that unsorted array. – spanky Sep 01 '17 at 19:56
  • In the array: [3, 5, 6, 8, 5, 3] 5 is duplicated _before_ 3. So the return value should be 5, not 3. –  Sep 01 '17 at 19:57
  • @user7496931: Oh, that's really quite different. You can keep the index of the last duplicated index found and continue on. Next time you find a duplicate, see if its index is lower than the previous one, and if so, replace it. Then after the loop, return the member at the last stored index. – spanky Sep 01 '17 at 20:00
  • @user7496931: I updated the answer but haven't verified with any tests beyond the one you gave, so you'll want to do some further testing and correction if needed. – spanky Sep 01 '17 at 20:06
1

You don't need a second loop, you could use Array.prototype.indexOf(). Also, you could reduce lines of code and return the result at once. For example:

function duplicateNumber(arr) {
  for (var i = 0; i < arr.length; i++) {
    if (arr.indexOf(arr[i]) !== i) {
      return arr[i];
    }
  }
  return null;
}

console.log(duplicateNumber([3, 5, 6, 8, 5, 3]));
sergdenisov
  • 8,327
  • 9
  • 48
  • 63
0
function getFirstDupe(arr){
  var dupe;

  if(arr && arr.forEach){
    arr.forEach(function(item, index){
      if(dupe === undefined && arr.indexOf(item) !== index){
        dupe = item;
      }
    })
  }

  return dupe;
}
julian soro
  • 4,868
  • 5
  • 28
  • 36
  • Please provide an explanation as well as just posting code. Your solution may be correct, but other readers will be interested in the approach – Mikkel Sep 02 '17 at 02:44
0

just an alternative approach, you can use an object to store the count

function duplicateNumber(arr) {
    let count = {};
    for (var i = 0; i < arr.length; i++) {
        count[arr[i]] = (count[arr[i]] || 0) + 1;
        if(count[arr[i]] > 1){
            return arr[i];
        }
    }
    return dup_num
}

console.log(duplicateNumber([0, 1, 1, 2, 3, 4, 4, 5]));
marvel308
  • 10,288
  • 1
  • 21
  • 32
0

If you want this in the shortest amount of time. I would suggest things like this. This will complete in worst case O(n);

function duplicateNumber(arr) {
  var memory = {};
  for (var i = 0; i < arr.length; i++) {
    if(memory[arr[i]] === undefined){
      memory[arr[i]] = true;
    }else{
      return arr[i]
    }
  }
}

var arr = [2,3,2,3];
console.log(duplicateNumber(arr));
Michael Warner
  • 3,879
  • 3
  • 21
  • 45
  • not really, look at https://stackoverflow.com/questions/7700987/performance-of-key-lookup-in-javascript-object – marvel308 Sep 01 '17 at 19:57
0

To elaborate on what spanky said: With your solution, you will at one point always end up comparing arr[i] with arr[j] while i being equal to j (therefore to itself). And because you don't stop your iteration when you find a match, my guess is that your function will always return the last element of your input array.

Aarkon
  • 484
  • 1
  • 6
  • 16
0
const solution = arr => {
const duplicates = ar => ar.filter((item, index) => ar.indexOf(item) !== index)
if (duplicates(arr)[0]) return duplicates(a)[0]
else return -1
}

First, filter out the duplicates by their indices then return the index 0 of arr.

Ramadhan
  • 11
  • 3
  • This will go trough whole array/list, fetch all duplicate values in a list. Then you hand pick single one from that array. There are far better ways to do it than what you have described. Those ones return first duplicate item they find, and are generally much faster. – Kadaj Jul 06 '22 at 07:47