Take the 2-minute tour ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free, no registration required.

My browser is crashing from this loop that doesn't appear to be unterminated.

function checkLetters(word){
    for(i=0;i<5;i++){
        for(j=i+1;j<5;j++){
            if(word.charAt(i) == word.charAt(j)){
                return false;
                break;
            } 
        }
    }
    return true;
}
var compLibrary = [];
for (var k=0;k<library.length;k++) {
    if(checkLetters(library[k]) == true) {
        compLibrary.push(library[k]);
    }
}

I am trying to search the library for words with no repeating letters and pushing it into a new array The whole library is five letter words.

share|improve this question
1  
I doubt that code freezes the browser, the checkLetters has constant (C ~ 25) time, so the entire snippet is O(n), where n is the size of library.length .. unless library.length is something ridiculously large, it should be complete in a very small amount of time. –  user2246674 Aug 9 '13 at 0:53
    
I guess, following with the above: if only checking the first, say, 100 words (;k<Math.min(library.length,100);), does it still "crash" the browser? –  user2246674 Aug 9 '13 at 1:00
    
Yes it's only 8000 words but this loop is freezing my browser and firebug is telling me it's "if(checkLetters(library[k]) == true)". I have many other statements where I am looping through the library and they all work its just it won't put words with no repeating letters into "var compLibrary" and my browser stops the script. –  Thomas Nocera Aug 9 '13 at 1:01
    
Can you post a minimal jsfiddle.net (with the word list or appropriate AJAX to fetch it) that shows the behavior? –  user2246674 Aug 9 '13 at 1:04
    
I've added an "answer" showing that the posted code is likely not the problem. Please provide a supporting minimal example showing my counter-example wrong. Here is the fiddle (again): jsfiddle.net/FqdX7 –  user2246674 Aug 9 '13 at 1:18

6 Answers 6

up vote 3 down vote accepted

It's not an infinite loop, but it does look like a pretty expensive operation. There's not any really elegant way to detect an infinite loop (or recursion) so most engines just resort to either

  1. Not trying to detect it, and running forever until some lower-level controller (like, the kernel) kills it.
  2. Automatically killing itself when it gets to a certain recursion depth, loop count, or run time.

Your algorithm loops 5 * 4 * library.length times, so depending on how long your library actually is, your code could certainly trigger #2. But there are faster ways to find duplicate letters:

function checkLetters(word) {
    var i=word.length;
    var seenChars={};
    var c;
    while (i-->0) {
      c = word.CharAt(i); # The current character
      if (c in seenChars) return false;
      seenChars[c] = 1;
    }
    return true;
}
var compLibrary = [];
for (var k=0; k < library.length; k++) {
    if (checkLetters(library[k]) == true) {
        compLibrary.push(library[k]);
    }
}
share|improve this answer
    
I have faith in modern CPUs. Even though there are nested loops, it's still in simple linear bounds due to the (small) fixed word size. I would suspect the browser/impl. in question would make a difference if a lookup like this was faster than the nested loops (because of the fixed bounds of the checkLetters loops). Although, since I don't have benchmarks on both code snippets in question I have no supporting evidence if this will or will not perform (suitably) better. –  user2246674 Aug 9 '13 at 1:08
    
Also, nit: I would recommend writing [stackoverflow] example code like i-- > 0 so as to not confuse people about the --> "operator" xD –  user2246674 Aug 9 '13 at 1:12
    
I reject that this answer benefits the question, as posted. I've created some counter fiddles to support my position. –  user2246674 Aug 9 '13 at 1:37
    
This works but I don't exactly know how, do you mind giving me an explanation? –  Thomas Nocera Aug 9 '13 at 1:49
    
@ThomasNocera Your original code works just fine as well. I've provided fiddle cases showing that something else is the problem. What it is, I don't know. But the onus is now on you to providing a minimal example of it "crashing" the browser. Anyway, this answer uses a object to "remember" which letters have already been seen - c in seenChars (it's almost the same as seenChars[c] !== undefined but checks for existence, not value) check this (returning false immediately if a seen letter is encountered), and seenChars[c] = 1 adds a seen character to the lookup. –  user2246674 Aug 9 '13 at 1:53

Shouldn't this loop

for(i=0;i<5;i++){
        for(j=i+1;j<5;j++){

be something in these lines

for(var i=0; i< word.length ;i++){
        for(j=i+1; j<word.length/2; j++){
share|improve this answer
    
all of the words are 5 letters so "word.length" doesn't matter, and the object of this loop is to compare each letter with the rest of the letters to make sure that they aren't the same so you end up with a word with all distinct letters like "proxy" –  Thomas Nocera Aug 9 '13 at 0:51
1  
Also you would not need a break statement –  Sushanth -- Aug 9 '13 at 0:51
    
You don't need to but it saves the computer a few miliseconds lol –  Thomas Nocera Aug 9 '13 at 0:54
1  
But the function will always return false before hitting that statement. –  Sushanth -- Aug 9 '13 at 0:56
    
@ThomasNocera your break statement saves the computer no time at all - if anything it costs the computer some (very small) time for the parser to read it just so the dead code eliminator can remove it. –  kojiro Aug 9 '13 at 1:59

Can't see what your issue is but here's the solution I suggest for your problem:

var words = ['hello', 'bye', 'foo', 'baz'];

function hasUniqLetters(word){
  var uniq = word.split('').filter(function(letter, idx){
    return word.indexOf(letter) == idx;
  });
  return uniq.length == word.length;
}

var result = words.filter(hasUniqLetters);

console.log(result); //=> ["bye","baz"]
share|improve this answer
function checkLetters(word){
    for(i=0;i<5;i++){      //both i and j were not instantiated
        for(j=i+1;j<5;j++){
            if(word.charAt(i) == word.charAt(j)){ //It could be crashing if
                return false;                     //word <= 5
                break;//a break after a return
            }         //statement is redundant.
        }
    }
    return true;
}

You must put var before declaring a variable.

word.charAt(i) can be written like word[i]

share|improve this answer
1  
Neither of these things answers the question why the browser would crash. –  kojiro Aug 9 '13 at 1:07
    
The code would not run if all of theses errors are in it. –  kiansheik Aug 26 '13 at 20:52

Try this:

function checkLetters(word){
  for(var i=0,j=1,l=word.length-1; i<l; i++,j++){
    if(word.charAt(i) == word.charAt(j)){
      return false;
    } 
  }
  return true;
}
var compLibrary = [];
for(var i=0,l=library; i<l; i++){
  if(checkLetters(library[i]) == true){
    compLibrary.push(library[i]);
  }
}
share|improve this answer
    
Getting this syntax error "SyntaxError: missing ) after for-loop control" –  Thomas Nocera Aug 9 '13 at 1:12

tldr; The code originally posted should not crash the browser.

The following explains why nested loops are not always bad for efficiency and shows a counter-example where the original code works successfully without crashing the browser when running over 100,000 simulated words.

The complexity of the posted code is low and it should run really fast. It executes here in a fraction of a second (under 20ms!), even at all "20 * 8000" - i.e. C * O(n). Note that the time complexity is linear because the nested loops in checkLetters have a constant time: due to this small fixed limit ("20 loops" max each call) it does not represent a performance problem here.

As such, I maintain that it is not an issue wrt it being an efficiency problem. I assert that the original code will not "crash" a modern browser environment. For longer or unbound words then using a (presumably) lower complexity probe attempt may pay off - but the inner loop runs in small constant time here. (Actually, due to distribution of letters within words and word lengths I would imagine that the constant rarely exceeds "90 loops" for a natural language like English.)

See http://jsfiddle.net/FqdX7/6/

library = []
for (w = 0; w < 100000; w++) {
    library.push((w + "12345").slice(0,5))
}
function checkLetters(word){
    for(i=0;i<5;i++){
        for(j=i+1;j<5;j++){
            if(word.charAt(i) == word.charAt(j)){
                return false;
            } 
        }
    }
    return true;
}
$('#time').text("running")
start = +(new Date)
var compLibrary = [];
for (var k=0;k<library.length;k++) {
    if(checkLetters(library[k]) == true) {
        compLibrary.push(library[k]);
    }
}
time = +(new Date) - start
$('#time').text(time + "ms")

On my machine (in Safari) the code runs in ~30 milliseconds (~40ms if the "return false" is removed) for an input of 100,000 words!

In comparison, the answer with a probe (seenChars lookup) actually runs worse in Safari/Chrome. See http://jsfiddle.net/Hw2wr/5/, where for 100k words it takes about 270ms - or about 9x slower. However, this is highly browser dependent and the jsperf in the comments shows that in Firefox the probing approach is faster (by about 2x) but is slower again in IE (say 4-5x).

YMMV. I think the original code is acceptable for the given situation and the "crashing" problem lies elsewhere.

share|improve this answer
    
I made my answer a CW so feel free to edit it. Also, here's some ammo. jsperf.com/is-jsperf-just-broken/3 –  kojiro Aug 9 '13 at 1:55
    
@kojiro Thanks for the jsperf. It made me completely have to revert my assumptions about browser performance (and last paragraph) and work on calling out different browsers specifically. It is amazing the Firefox fairs so much better (or webkit-based so much worse) for this specific use of probing. –  user2246674 Aug 9 '13 at 2:26

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.