Are there more expressive ways to do <, =, > conditions than if/elseif/else?

I’m using an iterative binary search to help find some stuff, and am wondering if anyone knows of a more expressive way to perform these greater than, equal to or less than statements.


...
while (low <= high && position === -1) {
    mid = Math.floor((low + high) / 2);
    if (haystack[mid] > needle) {
        high = mid - 1;
    } else if (haystack[mid] < needle) {
        low = mid + 1;
    } else {
        position = mid;
    }
}

If we use (haystack[mid] - needle) we can then end up with a condition that’s either positive, equal to, or negative; but other than going recursive on things, are there more expressive ways to make use of this kind of condition other than an if/elseif/else construct?

How about this?


...
while (low <= high && position === -1) {
    mid = Math.floor((low + high) / 2);
    diff = haystack[mid] - needle;
    position = mid + Math.max(-1, Math.min(diff, 1));
}

Basically I’m taking the difference between haystack[mid] and needle and then clamp it on the open interval (-1, 1) and then add that to position.
End effect is the same as your code, but without (explicit) if/then/else.

Are more readable version:


function clamp(val, min, max) {
    return Math.max(min, Math.min(val, max))
}
...
while (low <= high && position === -1) {
    mid = Math.floor((low + high) / 2);
    diff = haystack[mid] - needle;
    position = mid + clamp(diff, -1, 1);
}

Clamping is a good way to get a -1/0/+1 value, but without low or high being adjusted, means that we loop forever over the same area.

So, while using clamping we now end up with the following code:


while (low <= high && position === -1) {
    mid = Math.floor((low + high) / 2);
    direction = clamp(needle - haystack[mid], -1, 1);
    newPosition = mid + direction;
    switch (direction) {
    case +1:
        low = newPosition;
        break;
    case -1:
        high = newPosition;
        break;
    case 0:
        position = newPosition;
        break;
    default:
        // do nothing
    }
}

Can we refactor so this so that high and/or low are given in terms of mid, direction, and possibly distance? I’ll give that a shot.