views:

594

answers:

3

I am trying to optimize a function which does binary search of strings in Javascript.

Binary search requires you to know whether the key is == the pivot or < the pivot.

But this requires two string comparisons in Javascript, unlike in C like languages which have the strcmp() function that returns three values (-1, 0, +1) for (less than, equal, greater than).

Is there such a native function in Javascript, that can return a ternary value so that just one comparison is required in each iteration of the binary search?

+3  A: 

You can use the localeCompare() method.

string_a.localeCompare(string_b);

/* Returns:

 0:  exact match

-1:  string_a < string_b

 1:  string_b > string_b

 */

Further Reading:

Daniel Vassallo
Unfortunately, stringCompare is not reliable.Opera, IE, Firefox, Chrome and Safari all return 1 for'dog'.localeCompare('cat'), which is to be expected,and -1 when you reverse the caller and the argument.BUt capital letters behave oddly- 'dog'.localeCompare('Dog')Of the browsers I tested, only Safar 4 returned 1.It returns -1 in IE8 and firefox 3, and Opera 9 and Chrome both return +32.
kennebec
+1  A: 

You can use the comparison operators to compare strings. A strcmp function could be defined like this:

function strcmp(a, b) {
    if (a.toString() < b.toString()) return -1;
    if (a.toString() > b.toString()) return 1;
    return 0;
}

Edit    Here’s a string comparison function that takes at most min { length(a), length(b) } comparisons to tell how two strings relate to each other:

function strcmp(a, b) {
    a = a.toString(), b = b.toString();
    for (var i=0,n=Math.max(a.length, b.length); i<n && a.charAt(i) === b.charAt(i); ++i);
    if (i === n) return 0;
    return a.charAt(i) > b.charAt(i) ? -1 : 1;
}
Gumbo
But this routine does exactly what the OP doesn't want to do: there are two string comparisons (let alone those function calls to "toString").
Pointy
@Pointy: It’s not possible with just one comparison. You need at least min {`a.length`, `b.length`} steps (compare two characters at a time) to determine if the strings are equal or not. (Even `localeCompare` will do that internally.)
Gumbo
No, localeCompare will not do that internally. Comparing the characters is implemented as a subtraction, so as soon as there's a non-zero result of that operation you know the answer. Your answer can re-compare possibly *all* the characters of each string.
Pointy
@Pointy: But the substraction is done character by character. And that’s the point. That takes at *most* (and not at least as I wrote) min {`a.length`, `b.length`} steps (in the case where both strings are equal). But you’re right. It’s better to test for equality first as that takes the most steps.
Gumbo
@Gumbo localeCompare doesn't have to be in Javascript right? It could be natively implemented. Or I have missed something...
HRJ
Maybe I'm missing something. When you code up two completely separate Javascript string comparisons, what happens? Well, the first comparison gets started and (let's say) it notices a mismatch at character position 5. That is, characters 1 through 4 match, but character 5 mismatches. If the first Javascript comparison is a "!=" comparison (or "=="; whatever), then we know that the strings are not equal. However, in the low-level comparison code, we also know the answer to "<" and ">". But we throw away that knowledge when we start the next (Javascript) comparison, and re-compare chars 1-4.
Pointy
@Pointy: You probably meant something like the algorithm in my update, right?
Gumbo
@Gumbo yes, exactly. Of course, "localeCompare" is part of the runtime and therefore a C/C++ (or whatever) implementation. I wonder how good a job V8 would do at optimizing out the "charAt" calls?
Pointy
A: 

Well in JavaScript you can check two strings for values same as integers so yo can do this:

  • "A" < "B"
  • "A" = "B"
  • "A" > "B"

And therefore you can make your own function that checks strings the same way as the strcmp().

So this would be the function that does the same:

function strcmp(a, b)
{   
    return (a<b?-1:(a>b?1:0));  
}
Cipi
Again, read the original question!! The point is to avoid doing more than one string comparison.
Pointy
Oh sorry. Didn't see that... At least this works for someone. =|
Cipi