views:

485

answers:

7

I seem to handle special cases like this somewhat frequently. There's got to be a more concise syntax or construct:

var x = solveForX(); /* some slow calculation here */
if (x < 0)
{
    x = 0;
}

This is equivalent, but doesn't feel any more elegant:

var x;
x = (x = solveForX()) < 0 ? 0 : x;

Maybe there's a bit shift trick?


Update: I ran some benchmarks in Firefox to compare my two favorite answers - the one I accepted, and Peter Ajtai's. Turns out Peter's is quite a bit faster! Running 1,000,000 iterations of each (I also ran a version that caches Math.max to see how much time the lookup contributed) shows that Peter's runs in under half the time of the Math.max version, even with max caching.

That said, a million iterations even with the "slowest" method still takes only 0.25 seconds.

+27  A: 

How about

var x = Math.max(solveForX(), 0);
Dave McClelland
This is *the* way.
Andreas Rejbrand
FWIW, although this is more terse, as a reader of code, this form takes me several seconds longer to parse than the OP's original form.
quixoto
Not the clearest thing ever, using `Math.max` to implement a minimum.
Chris Burt-Brown
I *always* use max(x, 0). To me it is much more clear then the OP's original if-clause, which I need ~2 seconds to understand. To me min(x, 0) is a *very* standard thing.
Andreas Rejbrand
Is it intentional that you typed `min(x, 0)` the second time in your comment? Looks like an easy idiom to accidentally reverse.
Chris Burt-Brown
@Chris Burt-Brown: It is a typo. I meant "max(x, 0)" at both places.
Andreas Rejbrand
@Andreas - hehe.... I believe that's what's known as "game, set, and match" to @Chris ;) since his point was that accidentally using `min` would be a common typo with this method..... even though it is a pretty good solution overall.
Peter Ajtai
@Dave - [Your rep graph](http://stackoverflow.com/users/160300?tab=reputationhistory#tab-top) is pretty damn funny.
Matt Ball
@Bears, I know, today's been a pretty sweet day. 215 rep and 6 badges. Whoo
Dave McClelland
Yes, I see what you mean. But I still think that the `max(0, x)` approach is standard, and I use it all the time. So I do not need to think when I see this, in contrast to the longer `if` statement, that I need to think about. And similarly, `min(100, x)` will provide an upper bound for the result, and, most importantly, I often use `max(0, min(360, colourHue))` to employ both upper and lower bounds, for instance (in this case) when working with colour hues (which can either be truncated to [0, 360] or used in a periodic (modulus) sense -- this is an example of the former case).
Andreas Rejbrand
@Andraes Rejbrand, Why would you want an upper and lower bound on color hues? -360 degrees = 0 degrees = 360 degrees = 720 degrees. Wouldn't the modulus operator do the job more accurately? (i.e. `colorHue % 360`)
kingjeffrey
@kingjeffrey - depends on the use/needs. He did say truncation *or* modding.
Matt Ball
+9  A: 

Something like:

x = Math.max(0, solveForX());
Vladimir
This is *the* way.
Andreas Rejbrand
A: 

Why do you have a problem with the first example? Although there's a few more lines, it's a lot easier to read and follow.

Ash Burlaczenko
This is a comment, not an answer, but I'll bite - I'm just curious.
Matt Ball
I think that `Math.max(0, solveForX());` is more clear and easier to read.
Andreas Rejbrand
This is a fine answer and does not belong as a comment. The question was: "Is there a better way". The answer is "No" (although admittedly passive-agressive in its wording).
kingjeffrey
@kingjeffrey - except that, as it turns out, there are multiple better/different ways.
Matt Ball
If the browser compiles both examples the same way, the example with less code is superior, because it will download faster. (Hmm... Occam's JavaScript razor?)
Matt
@Bears will eat you, Different? Yes. Better? That is a judgement call. I very much like Peter Ajtai solution. It's pretty slick. But for long-term maintainability by unknown future developers, Ash Burlaczenko has a point. Your original code communicates your logic in a very clear, understandable way. The `Math.max` solution (and Peter Ajtai's solution for that matter), while cool and concise, require future developers to grok a lower limit (Math.max) or a unusual assignment (Peter). This may not be initially obvious, and certainly requires more thought. I'd opt for obvious.
kingjeffrey
Sorry if i came across a bit agressive, i wasn't meaning to be :( @kingjeffrey, that was my initial thought. On projects that my span years it's expected that different developers will work on the same piece of code. That's why it's important to follow one standard and make you code and understandable as possible.
Ash Burlaczenko
@Ash Burlaczenko, "passive agressive" is quite different than "agressive". Perhaps "indirect" is more accurate. Regardless, your answer is the best one here, even if it is not reflected in the upvotes or the checkmark.
kingjeffrey
+6  A: 
(x < 0) && (x = 0);

Edit: Removed the if statement. Thanks Andreas.

This is one line, and it's clear what it does (in my personal opinion) - if you're familiar with boolean short circuit evaluation.

The above makes use of boolean short circuit evaluation. This can be very useful in certain situations (especially pointer arithmetic in C++, but boolean short circuit evaluation also works in Javascript).

x = 0 only evaluates if x < 0.

Here are two examples:

This alerts 1:

<script type="text/javascript">
    var x = 1;
    (x < 0) && (x = 0);
    alert(x);
</script>

This alerts 0:

<script type="text/javascript">
    var x = -1;
    (x < 0) && (x = 0);
    alert(x);
</script>
Peter Ajtai
Seeing that made my eyes hurt!
Peter M
x = 0 will always assign never evaluate
Woot4Moo
negative ghostrider
Woot4Moo
@Woot4Moo - Maybe in javascript but not in all languages. @PeterM - Agreed. The statement will never be true!!!!!
Ash Burlaczenko
I was pretty confused about this one. Also, it's an if statement with an empty body - so there would be no point in testing it anyway.
Matt Ball
@Ash @Woot4Moo What are you talking about? I put two examples up to prove that things evaluate as they should.
Peter Ajtai
@Peter Ajtai your original post was only that ghastly if statement. @Ash in what language is == not comparison?
Woot4Moo
@Peter, this is less clear to most people than a one-line version of the `if` in my question, e.g. `if (x < 0) x = 0;`.
Matt Ball
@Bears will eat you - Well... apparently... wow... I guess I'm the only one that thinks this is clear.
Peter Ajtai
This one should work perfectly (as long as JavaScript employs boolean short-circuit evaluation -- I do not know JS very well). But it is *much* less clear than the **standard** `max(x, 0)` approach. I'll give a +1 just to compensate for the unexplainable downvote, though.
Andreas Rejbrand
@Peter: Well, I just don't know why you'd ever use that kind of short-circuiting combined with an empty `if` body instead of the usual structure. But hey - I didn't downvote your answer.
Matt Ball
@Bears - Well, I really thought it was clearer. Guess most people don't think so ;)
Peter Ajtai
Please leave an explanation if you down vote. OP asked for more concise, and I reduced 4 lines into 1.
Peter Ajtai
Andreas Rejbrand
@Andreas - Thanks, that's a good suggestion. It does work.... changed the code to reflect it.
Peter Ajtai
+1 This is much more common in Ruby than JavaScript, but I like it!
kingjeffrey
@kingjeffre: This is much more common in Lisp than Ruby, and I like it.
Török Gábor
+1  A: 

I'd decorate the original solveForX function.

function returnNonNegative(fn) {
    function _f() {
        var x = fn();
        if (x < 0) {
            x = 0;
        }
        return x;
    }
    return _f;
}

solveForX = returnNonNegative(solveForX);

In this particular case, using Math.max seems to be fine, but this pattern provides a generic solution for this type of problems.

Török Gábor
I think that this question is about the `x = (x = solveForX()) < 0 ? 0 : x;` statement by itself, not about the `solveForX` function or the OP's context.
Andreas Rejbrand
@Andreas Rejbrand: the question was "is there a slicker way" of writing the construct above that consists of a function call and some additional behavior. I think this is the typical case for the decorator pattern. Syntax can be improved several ways but this makes semantics more clear.
Török Gábor
A: 

If you want to get real geeky with this, make a prototype for the number object. You will maintain the value, but the special formatted output for negatives could be managed easily with some output method.

http://www.w3schools.com/jsref/jsref_prototype_num.asp

Please don't do this! Avoid altering the native objects' prototype.
Török Gábor
@Török: agreed.
Matt Ball
Except when doing super fun things like creating a frameworkhttp://www.prototypejs.org/api/number
@user257493: some frameworks do it, others not. It can be a design choice when creating a framework but should not be used for such a problem like this.
Török Gábor
A: 

The accepted answer is perfect. If you want to accomplish the same without a function call, I believe that this is most concise:

var x;
(x = solveForX()) > 0 || (x = 0);

(In Safari, this implementation is a whole 7% faster than Math.max(), which is probably not worth caring about)

Sidnicious
Math.max(beyond,thunderdome) has better fight scenes.
Matt
`var Mad = Math; var x = Mad.max(beyond, thunderdome);` FTFY
Matt Ball