简体   繁体   中英

Javascript code infinite loop

I'm making a function, which takes an array of values and returns an array with only unique values. For example:

var strings = ["audi", "audi", "bmw", "bmw","bmw","bmw","audi","audi", "8-()"];

Result should be:

alert( unique(strings) ); // audi, bmw, 8-()

I don't understand why my function goes into infinite loop, could anyone help please? Here is the function:

function unique(arr) {
   var result = [];
   result.push(arr[0]);

   for (var i = 1; i < arr.length; i++) {
       for (var j = 0; j < result.length; j++) {
           if  (result[j] != arr[i]) {
              result.push(arr[i]); 
           }
       }
   }

   return result;
} 

There is no need for nested loop. you can check the result for available values with Array.prototype.indexOf or Array.prototype.includes methods.

 var strings = ["audi", "audi", "bmw", "bmw", "bmw", "bmw", "audi", "audi", "8-()"]; function unique(arr) { var result = []; result.push(arr[0]); for (var i = 1; i < arr.length; i++) { if (result.indexOf(arr[i]) === -1) result.push(arr[i]); } return result; } console.log(unique(strings)) 

BTW chef suggest is:

 var strings = ["audi", "audi", "bmw", "bmw", "bmw", "bmw", "audi", "audi", "8-()"]; console.clear(); var result = strings.filter(function(s){ return this[s] ? false : (this[s] = true); }, Object.create(null)) console.log(result); 

You can make it very simpler with Set and spread operators belongs to ES6,

var unique = src => [...new Set(src)]

By the way your logic is wrong. It will not run into an infinite loop. But it will give you an undesirable result.

You can make it simpler: using Array.reduce

 var values = [1,2,3,4,5,5,5,5,5,6,6,6,62,2,2] function getUniq(array) { return array.reduce(function(u, value) { if (u.indexOf(value) < 0) u.push(value) return u; }, []) } console.log(getUniq(values)) 

You can reduce the time complexity of this

var strings = ["audi", "audi", "bmw", "bmw","bmw","bmw","audi","audi", "8-()"];

var unique = [];

strings.forEach(function(str){
  if (unique.indexOf(str) < 0) {
     unique.push(str)
  }
});

return unique;

You can use Set also

var strings = ["audi", "audi", "bmw", "bmw","bmw","bmw","audi","audi", "8-()"];
var uniq = new Set(strings);

// if you need in array 
uniq = Array.from(uniq);

You could also use a dictionary/hashtable for this. Then the total runtime should be O(N) for looping through arr once:

function unique(arr) {
    unique = {}
    for(var i=0; i<arr.length; i++) {
        unique[arr[i]]=true
    }
    return Object.keys(unique)
}   

JSFiddle

You could change the inner loop a bit and use a variable found for checking if the value is in the result array.

This variable is also needed to check if the value is to push to the result set.

Additionally you could use a break to exit the inner loop if a duplicate value is found.

 function unique(arr) { var result = [], found; result.push(arr[0]); for (var i = 1; i < arr.length; i++) { found = false; // initial set to false for (var j = 0; j < result.length; j++) { if (result[j] === arr[i]) { found = true; // set to true to skip push break; // exit loop } } found || result.push(arr[i]); // push only, if not found } return result; } var strings = ["audi", "audi", "bmw", "bmw", "bmw", "bmw", "audi", "audi", "8-()"]; console.log(unique(strings)); // audi, bmw, 8-() 

A shorter ES6 proposal with Array#filter and a hash table as closure.

 function unique(arr) { return arr.filter((temp => a => !temp[a] && (temp[a] = true))(Object.create(null))); } var strings = ["audi", "audi", "bmw", "bmw", "bmw", "bmw", "audi", "audi", "8-()"]; console.log(unique(strings)); // audi, bmw, 8-() 

another fastest way, it is taking 2 milli seconds check the performance

var dupEleArr = [1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 2, 2, 2, 20, 3, 3, 3, 32, 324, 5, 52];
var uniqueArr = dupEleArr.sort(function(a,b){
    return a-b;
}).filter(function(ele,i,arr){
   return arr.length > 3 ? arr[i] != arr[i+1] : (arr.length == 2) ? (arr[i] != arr[i+1]) : true;
});

check jsfiddle

push() Pushes a new object to the beginning of an array and pushes everything back!

Let's view the state of your arrays in the program:

arr = [A,B]    result = [A]    i = 1

j = 0 -> arr[i] = B and result[j] = A    
so we use push: result = [B,A] and j++

j = 1 -> arr[i] = B and result[j] = A
so we use push: result = [B,B,A] and j++

j = 2 -> arr[i] = B and result[j] = A
so we use push: result = [B,B,B,A] and j++

Your array keeps increasing and the check will never fail, since you append to the beginning of your array and not the end.

But even if you appended your new Item to the end, you would not get unique results, since you have to check every element in result and only if not any of the results is the same you should add your new element to the result array.

For better solutions see the other answers -> Use a Set with contains()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM