4

I'm trying to practice for an interview and found a challenge online to write a function that will take an array of numbers and only return values that exist just once in the array, and return those values in order. For example, the array [1, 3, 5, 6, 1, 4, 3, 6] should return [4, 5].

I have a script that is passing the tests but for some of the tests is running too slow. Am I going about this wrong? Is there some fundamental way to speed this up? The script starts with findTheNumbers, and a is the array input:

function findTheNumbers(a) {
    var retVal = [];
    var nonUnique = [];

    for (var i = 0; i < a.length; i++){
        var isUnique = true;
        if (i != 0){
            for (var j = 0; j < nonUnique.length; j++){
                if (a[i] == nonUnique[j]){
                    isUnique = false;
                    break;
                }
            }
        }

        if (isUnique){
            for (var k = 0; k < a.length; k++){
                if (a[i] == a[k] && i != k){
                    isUnique = false;
                    nonUnique.push(a[i]);
                    break;
                }
            }    
        }

        if (isUnique){
            retVal.push(a[i]);
            if (retVal.length == 2){
                break;
            }
        }


    }

    retVal = sortArrayOfLengthOfTwo(retVal);
    return retVal;
}

function sortArrayOfLengthOfTwo(array){
    var retVal = [];
    if (array[0] > array[1]){
        retVal.push(array[1]);
        retVal.push(array[0]);
    } else {
        retVal = array;
    }

    return retVal;
}

UPDATE -

Not sure where the best place for this is, but here is my new version based on the accepted answer's hints (which worked SO much faster):

function findTheNumbers(a) {
    var retVal = [];
    var dict = {};
    for (var i = 0; i < a.length; i++){
        dict[a[i]] = 1 + (dict[a[i]] || 0);
    }

    for (var key in dict){
        if (dict[key] == 1){
            retVal.push(parseInt(key));
        }
    }

    return retVal;
}
cweiske
  • 30,033
  • 14
  • 133
  • 194
the-nick-wilson
  • 566
  • 4
  • 18
  • 1
    Take a profiler and profile to see why it's slow. PS: the "expected" solutions for the problem might be: 1) sort then check 2) use `Set` to "remember" numbers already met. Either would help you decrease the O(n^2) complexity of your current solution – zerkms Mar 16 '17 at 02:56
  • 2
    its' slow because you've loops within loops. that worsens run time performance. But that's only true for an array that has huge amount of data and you're doing a linear search. in this case it might not exactly be that reason. – Ousmane D. Mar 16 '17 at 02:58
  • 2
    *"return those values in order. For example, the array [1, 3, 5, 6, 1, 4, 3, 6] should return [4, 5]."* - I would have thought "in order" meant "in the order in which they appeared in the original array", not "in ascending order", but if you do want to sort them use the `.sort()` method. Your `sortArrayOfLengthOfTwo()` only works for the specific example input that happens to have only two items in its output, but other input could have an output of only one item, or ten items... – nnnnnn Mar 16 '17 at 03:02
  • Thank you all - I haven't used a profiler but will look into that. However, I am using my nonUnique array to track numbers already deemed duplicate. As far as the sorting, that's not necessarily my focus. Yes, I meant "in ascending order." Also the limits on the inputs for this challenge guarantee there will only be two unique outputs, so I took the easy way out with that function. – the-nick-wilson Mar 16 '17 at 03:07
  • 2
    "so I took the easy way out with that function" --- it is not. The generic solution would be much easier to read. – zerkms Mar 16 '17 at 03:11
  • Using .sort(), you mean? – the-nick-wilson Mar 16 '17 at 03:12
  • A generic solution that didn't break out of the loop as soon as two items were found would probably be easier to read, but definitely a generic sort is easier to read. To sort an array of numbers, `arr.sort(function(a,b) { return a-b })`. As far as writing more efficient code, there are other questions about counting unique array values, e.g., [this one](http://stackoverflow.com/questions/15052702/count-unique-elements-in-array-without-sorting). (Both answers in that question have a simple, short solution, but unfortunately not much explanation.) – nnnnnn Mar 16 '17 at 03:13
  • That link helped a ton (in combination with the answer I accepted) - thank you. I updated my question with the working function. – the-nick-wilson Mar 16 '17 at 03:54

1 Answers1

5

When a program is too slow, the rule of thumb is to

  • first blame your algorithm
  • next blame your implementation (edit: as suggested by Bergi)
  • finally blame the language

In your case, you parse the whole array n times, that is, the complexity is o(n^2). You can do the same with only one full parse, and a dictionary of the count for each element.

Edit about your new algorithm

Your new algorithm is very good. For the sake of your interview I have some comments:

  • instead of var, you should consider using const as much as possible, and let otherwise
  • do not hesitate to put intermediate results within variables
  • use more descriptive names for your variables

Here's the way I would implement it:

function findTheUniqueNumbers(a) {
  const numberCounts = {} // I prefer "new Map()" on recent env.
  a.forEach( function(e) {
    const previousCount = numberCounts[e] || 0
    numberCounts[e] = previousCount + 1
  } )

  return Object.keys(numberCounts)
    .filter( function(e) {
      return numberCounts[e] === 1
    } )
}


const uniqueNumber = findTheUniqueNumbers([1, 3, 5, 6, 1, 4, 3, 6])
console.log(uniqueNumber)
Antoine Trouve
  • 1,198
  • 10
  • 21
  • I think I understand what you're saying (sorry - I don't have a CS degree, and I'm still learning). Let me see if I can implement what I think you're saying... – the-nick-wilson Mar 16 '17 at 03:11
  • 1
    Blame the language? Really? I've never seen a general-purpose language that would be to blame per se when a program runs too slow. (Oh, maybe with the exception of PHP :-D) Imo, the second step should be "*blame your implementation*". – Bergi Mar 16 '17 at 03:48
  • This answer gave me just enough (combined with one of the comments above that pointed to this: http://stackoverflow.com/questions/15052702/count-unique-elements-in-array-without-sorting ) to help me figure this one out. It now runs super fast using this approach. – the-nick-wilson Mar 16 '17 at 03:52
  • 1
    PS - Definitely blaming the algorithm (or my implementation) on this one. Once I knew this, JavaScript could easily handle it. – the-nick-wilson Mar 16 '17 at 03:55
  • 1
    Glad to hear that was useful. Good luck with your interviews :) – Antoine Trouve Mar 16 '17 at 04:01
  • 1
    I'm not a fan of blaming things. Rather - and this really works in practice! - use tools, logical reasoning, and guided methodology to diagnose and fix problems. Then you'll know what to "blame" and have evidence supporting the claim - and even plans for a solution! (It may help to stop following the circus on TV / Twitter / Facebook.) – user2864740 Mar 16 '17 at 04:01