60

I have this ugly code:

if ( v > 10 ) size = 6;
if ( v > 22 ) size = 5;
if ( v > 51 ) size = 4;
if ( v > 68 ) size = 3;
if ( v > 117 ) size = 2;
if ( v > 145 ) size = 1;
return size;

How can I get rid of the multiple if statements?

Josh Lee
  • 171,072
  • 38
  • 269
  • 275
kofucii
  • 7,393
  • 12
  • 51
  • 79
  • 4
    Did you really mean 5 and 6 to be in that order? – Jon Skeet Sep 24 '10 at 11:04
  • 1
    No i didn't, my mistake. – kofucii Sep 24 '10 at 11:07
  • 62
    FWIW, I don't think that is particularly ugly. It's easy to see what is happening, it's trivial to add in more cases, it's clear. The only thing I'd consider doing is adding a list where the conditions/values are stored, but other than that, I have no problem with it. – Noon Silk Sep 24 '10 at 11:12
  • All these answers and no one chose a switch statement, which is clearly better suited for this. – Stephen Sep 24 '10 at 14:09
  • 3
    How so? It's not an '==' comparison. – Left For Archive Sep 24 '10 at 15:05
  • 13
    This code isn't ugly! A "fancier" solution will only make your code more unreadable and harder to maintain in the future. – Mikael Sundberg Sep 24 '10 at 15:33
  • 7
    @Stephen: I don't see why a switch with 145 cases is less ugly. Look closer, a `>` is been used as equation, not `==`. – BalusC Sep 24 '10 at 16:35
  • 1
    So what does the code do if v <= 10? – Jim Ferrans Sep 25 '10 at 03:14
  • 3
    You don't mention where do you need this type of code. All these magic numbers make me wonder where is this used. – cherouvim Sep 25 '10 at 15:07
  • This question is linked in daily WTF (hence a lot of views): http://forums.thedailywtf.com/forums/p/20192/234306.aspx One has over there suggested a `switch` inside a `while`, which isn't answered before in this topic. I am not sure if I like it more than the ternary operator one. It's also less efficient. – BalusC Sep 27 '10 at 14:27
  • the switch inside the while is huge overhead (yeah I know, I should be talking). Comparing is the way to go, which ever way you do it. But imagine the waste of cycles for a loop if the numbers where further apart. – Sean Patrick Floyd Sep 27 '10 at 15:23
  • 2
    For anyone tempted to take the loop-and-switch design seriously -- _it's a joke_! That's one of DailyWTF's classic gags, like `enum BOOL { TRUE, FALSE, FILE_NOT_FOUND };`. (Yeah, I know, explaining the joke is lame. But I don't want to maintain the code of some poor sap that took it seriously.) – Steve S Sep 27 '10 at 16:46
  • 3
    IMO what's ugly about the code is the use of magic numbers / hardcoded constants. For my eyes the multiple IFs aren't a problem, they're simple efficient and readable, just those constant values that are unreadable... – Richard Harrison Sep 28 '10 at 14:43
  • 3
    This question has garnered a pretty large number of creative answers that are all *really bad ideas in real-life programming*... – Timwi Oct 10 '10 at 18:01
  • 20
    So many upvotes on such a simple answer to a simple question, when the actual hard questions and answers don't get upvoted that much. Is StackOverflow dysfunctional? – Chris Dennett Oct 10 '10 at 20:36
  • @Chris I appreciate your comment, but it is the wish and like of others what they want to do. – RTA Jun 21 '12 at 12:03
  • READABILITY on if..then..else construction in Java(!?) This question is perfect example of providing a piece of code as example and still managing to make a non-constructive/controversial/offtopic. If you don't like if..then..else - do your own DSL development, if you don't know about existence of switch ... I guess - learn Java keywords. I can only help by downvoting – Yauhen Yakimovich Nov 29 '12 at 22:53
  • Giving a second thought, this question does have its own special way of stimulating creativity.. a very strange and unusual effect (a coding contest on mindless problem). How about tagging this question "ugly"? – Yauhen Yakimovich Nov 29 '12 at 23:27

25 Answers25

160

How about such approach:

int getSize(int v) {
    int[] thresholds = {145, 117, 68, 51, 22, 10};

    for (int i = 0; i < thresholds.length; i++) {
        if (v > thresholds[i]) return i+1;
    }
    return 1;
}

Functionally: (Demonstrated in Scala)

def getSize(v: Int): Int = {
  val thresholds = Vector(145, 117, 68, 51, 22, 10)
  thresholds.zipWithIndex.find(v > _._1).map(_._2).getOrElse(0) + 1
}
missingfaktor
  • 90,905
  • 62
  • 285
  • 365
mfloryan
  • 7,667
  • 4
  • 31
  • 44
  • 10
    Nice thing about this approach is that it can be easily extended to more steps or wider range without much effort. – sbass Sep 24 '10 at 11:53
  • 1
    +1 this is the most elegant non-object-oriented solution here :-) – Sean Patrick Floyd Sep 24 '10 at 13:37
  • 2
    Missing a last return statement, this won't compile (in java) – Ishtar Sep 24 '10 at 13:42
  • +1 good enough. @seanizer - once you get to the hardware, it's all non-OO anyway ;-) – Tony Ennis Sep 24 '10 at 14:18
  • Good call... definitely not something I've seen before. – WernerCD Sep 24 '10 at 16:36
  • 5
    I like this, too. Just to be safe I would put a comment in case someone adds/changes `steps` without realizing order is important. Alternately, a reverse sort on `steps` before the loop would work, too. – D'Arcy Rittich Sep 24 '10 at 17:44
  • I would have returned `6` and not `1` at the end if there is no match (while there is bug in the original implementation `if v <= 10`) – Déjà vu Sep 25 '10 at 15:35
  • 7
    This is less readable. With the if/elseif solution the code is instantly understandable. This code, while easy to understand, still requires a little thought to grok. Exact same number of lines of code, but requires a tiny bit more effort to read == less optimal solution IMO. – Bryan Oakley Sep 30 '10 at 17:03
  • 2
    I dislike the idea, what if you have to change and for 22 return 17 and 51 return 50? code does change and this is a maintenance nightmare. – IAdapter Oct 05 '10 at 14:52
  • I have to say, I had to think longer to understand the original code than I had with this loop. – Johannes Schaub - litb Oct 10 '10 at 20:45
  • make the loop a binary search and you are even better – stryba Feb 26 '12 at 13:18
  • @IAdapter If it were arbitrary numbers that might be a concern, but the idea of thresholds (think experience levels in a game, or alert limits on pressure valve) dictates the levels will be sequential and non-overlapping. – corsiKa Dec 07 '12 at 17:12
  • @corsiKa example requiremtn - if you pay $200 the code will add two levels. if you hit level 100 - you get 2 levels extra. just 2 extra ifs, right? – IAdapter Dec 07 '12 at 17:54
  • @IAdapter Yes and no. You need those extra if statements in the original code as well. The point of the post is to replace the if statements with an array of values to search (which actually extends itself to a binary search instead of a linear search, but that's an exercise for the reader). Rolling it into a loop doesn't solve every problem, but it does remove the need for boilerplate if statements. (Ironically, it will probably be unrolled by the compiler, but that's another story.) – corsiKa Dec 07 '12 at 17:59
88

Using the NavigableMap API :

NavigableMap<Integer, Integer> s = new TreeMap<Integer, Integer>();
s.put(10, 6);
s.put(22, 5);
s.put(51, 4);
s.put(68, 3);
s.put(117, 2);
s.put(145, 1);

return s.lowerEntry(v).getValue();
barjak
  • 10,842
  • 3
  • 33
  • 47
  • 2
    If you leave the keys as the OP did, you can use lowerEntry instead of floorEntry. That way the data is consistent w/ OP. – basszero Sep 24 '10 at 11:24
  • 5
    very nice solution (+1), in fact I'd say it's the only non-ugly solution here – Sean Patrick Floyd Sep 24 '10 at 11:38
  • 11
    Well, very non-ugly until you look at the overhead. – JUST MY correct OPINION Sep 24 '10 at 12:00
  • I think `HashMap` would be better solution if you are fix with map only. – jmj Sep 24 '10 at 12:16
  • 4
    A `HashMap` doesn't work here. `NavigableMap` allows to find the nearest key (either lower or greater) for a given value, which is important in our case. – barjak Sep 24 '10 at 12:23
  • 14
    @JUST : I confess I wouldn't use my solution in such a simple case, but rather a bunch of if statements. However, if some flexibility is needed (for example, if those numbers are not hard-coded but come from a configuration file), then this solution makes sense. Plus, this solution can be useful if the "f" key of my keyboard is broken :) – barjak Sep 24 '10 at 12:31
  • @JUST well there's always a tradeoff between code usability and performance. This is highly usable object oriented code, so naturally there is overhead. If you don't want overhead, stick with if/else horror. – Sean Patrick Floyd Sep 24 '10 at 13:37
  • 2
    That's actually a very ugly solution. Simpler is always better. Why would you abuse TreeMap to simulate ifs? A general solution is to put the values in an array and do binary searching. – Alexandru Sep 24 '10 at 13:48
  • 6
    Rules of Abstraction: Rule 1: Don't do it. Rule 2 (for experts only): Don't do it yet. – Left For Archive Sep 24 '10 at 14:23
  • 1
    @seanizer: There is nothing particularly object-oriented about using a hash map or equivalent. I was using those kinds of data structures before I'd even caught the faintest whiff of OOP back in the '80s. – JUST MY correct OPINION Sep 24 '10 at 14:42
  • 1
    @JUST It's more object-oriented to use a custom object (see my solution) but less object-oriented to use if/else blocks. And I doubt that you used anything similar to the java map interface 20-30 years ago. Yes, you probably used hash tables, but you didn't have a clean interface that abstracts from the workings underneath (which are by no means equivalent, there are big differences between hash and tree based lookup). – Sean Patrick Floyd Sep 24 '10 at 15:39
  • 3
    @badp if you think so, then OOP is clearly not the right concept for you (and hence java is not the right language) – Sean Patrick Floyd Sep 24 '10 at 23:31
  • Interesting piece of code, I had never seen NavigableMap before. Good thing to know, thanks. – mattjames Sep 25 '10 at 05:36
  • 1
    @seanizer, I like OOP, but I think you're going a bit _too_ far with it there. I mean, setting up a _data structure_ then _navigating_ it just to make some range checks? – badp Sep 25 '10 at 07:18
  • 2
    @seanizer: please learn some history, and preferably some theory too. Abstract data types do not require OOP, and the concept of presenting a clean interface that abstracts from the workings underneath has been around for at least 50 years. – Porculus Sep 25 '10 at 13:58
  • @badp I think that's what stinks about java: it gives you the choice to do things the OOP way or not. If java were a real OOP language (and didn't try to be a C dialect and an OOP language at the same time) everything would be an object, there would be no primitives and hence there would be no *should I use OOP here* question. – Sean Patrick Floyd Sep 25 '10 at 15:47
  • @Porculus *Don't know much about history, Don't know much biology, Don't know much about a science book, Don't know much about the French I took ...* Abstract Data Types do not require OOP, and I never said so, either. It would be a lot easier to do this kind of code in a functional language like Haskell or Erlang and even in a preudo-functional one like Scala or Groovy. But the topic is Java and in Java, OOP is the most elegant way to go. – Sean Patrick Floyd Sep 25 '10 at 15:58
  • @JUST overhead? maybe for 6 values... but in the big picture, the navigable map will scale up nicely and the chained if-else won't. If you want something more efficient hard-coded, the if-else's will need to have a tree structure to perform an equivalent binary search. – fortran Sep 28 '10 at 08:59
  • @fortran: If you are in a position where you're making even chains of **six** if/else statements (or their equivalents) you need to rework your design quickly in 99.44% of cases. – JUST MY correct OPINION Sep 30 '10 at 08:17
  • 1
    @JUST I don't get your point. There are many things out there that do not follow simple and regular rules and need to be mapped or looked up with tables/switch's/if-else's or whatever. – fortran Sep 30 '10 at 10:26
82

The most obvious problem with the OPs solution is branching, so I would suggest a polynomial regression. This will result in a nice branchless expression on the form

size = round(k_0 + k_1 * v + k_2 * v^2 + ...)

You will of course not get an exact result, but if you can tolerate some deviance it's a very performant alternative. Since the 'leave unmodified' behavior of to original function for values where v<10 is impossible to model with a polynomial, I took the liberty of assuming a zero-order hold interpolation for this region.

For a 45-degree polynomial with the following coefficients,

-9.1504e-91 1.1986e-87 -5.8366e-85 1.1130e-82 -2.8724e-81 3.3401e-78 -3.3185e-75  9.4624e-73 -1.1591e-70 4.1474e-69 3.7433e-67 2.2460e-65 -6.2386e-62 2.9843e-59 -7.7533e-57 7.7714e-55 1.1791e-52 -2.2370e-50 -4.7642e-48 3.3892e-46 3.8656e-43 -6.0030e-41 9.4243e-41 -1.9050e-36 8.3042e-34 -6.2687e-32 -1.6659e-29 3.0013e-27 1.5633e-25 -8.7156e-23  6.3913e-21 1.0435e-18 -3.0354e-16 3.8195e-14 -3.1282e-12 1.8382e-10 -8.0482e-09 2.6660e-07 -6.6944e-06 1.2605e-04 -1.7321e-03 1.6538e-02 -1.0173e-01 8.3042e-34 -6.2687e-32 -1.6659e-29 3.0013e-27 1.5633e-25 -8.7156e-23 6.3913e-21 1.0435e-18 -3.0354e-16 3.8195e-14 -3.1282e-12 1.8382e-10 -8.0482e-09 2.6660e-07 -6.6944e-06 1.2605e-04 -1.7321e-03 1.6538e-02 -1.0173e-01 3.6100e-01 -6.2117e-01 6.3657e+00

, you get a beautifully fitted curve:

alt text

And as you can see, you get an 1-norm error of just 1.73 across the whole range from 0 to 200*!

*Results for v∉[0,200] may vary.

Alexander Torstling
  • 18,552
  • 7
  • 62
  • 74
  • I love this because I've always enjoyed coming up with expressions that "pack" the data for a lookup into mathematical expressions. Take some constants, throw in some multiplication, addition, modulus, abs(), floor(), and so on, and you can do fun stuff to calculate things like the number of days in each month. But I never considered a polynomial for this sort of thing! – ErikE Sep 29 '10 at 07:50
  • 2
    Wow! Could You elaborate more on the tools You used to produce the output? – Rekin Oct 10 '10 at 11:16
  • How did you find out the coefficients? Is it some advanced math? – Johannes Schaub - litb Oct 10 '10 at 21:34
  • It's just a polynomial regression, see http://en.wikipedia.org/wiki/Polynomial_regression. I used Octave to write the output function and computed a best fit using the polyfit function. – Alexander Torstling Oct 13 '10 at 15:34
  • If you round to nearest int after calculating, you should get near perfect results. – Thomas Ahle Oct 15 '10 at 15:24
  • 2
    +1 very good but it will be somewhat hard to understand what it does and harder to maintain. – fmucar Feb 02 '11 at 18:02
75
if ( v > 145 ) size = 1;
else if ( v > 117 ) size = 2;
else if ( v > 68 ) size = 3;
else if ( v > 51 ) size = 4;
else if ( v > 22 ) size = 5;
else if ( v > 10 ) size = 6;

return size;     

This is better for your case.

Optionally you should choose Switch Case where ever possible

Update: If you have analyzed the value of 'v' generally resides in lower range(<10) in most of the cases than you can add this.

if(v < 10)           size = SOME_DEFAULT_VALUE;
else if ( v > 145 )  size = 1;
else if ( v > 117 )  size = 2;
else if ( v > 68 )   size = 3;
else if ( v > 51 )   size = 4;
else if ( v > 22 )   size = 5;
else if ( v > 10 )   size = 6;   

further : You can also alter the condition sequence, according to your analysis. If you know that most of the values are less than 10 and then in the second place most of values lie between 68-117, you can alter the condition sequence accordingly.

Edits:

if(v < 10)           return SOME_DEFAULT_VALUE;
else if ( v > 145 )  return 1;
else if ( v > 117 )  return 2;
else if ( v > 68 )   return 3;
else if ( v > 51 )   return 4;
else if ( v > 22 )   return 5;
else if ( v > 10 )   return 6;   
jmj
  • 237,923
  • 42
  • 401
  • 438
  • "Optionaly you should choose Switch Case where ever possible" why ? – NimChimpsky Sep 24 '10 at 12:24
  • @NimChimpsky It would be more `readable`. I don't say that it is better considering performance, It depends on many factors. – jmj Sep 24 '10 at 12:28
  • 94
    Even better: just return the value right away without using intermediate `size` variable. – Rene Saarsoo Sep 24 '10 at 13:06
  • @NimChimpsky - In this case, the switch statement would have 145 cases or more, and that would make it pretty unreadable. (Especially if the reader needs to consider the case where the programmer has mistyped one of this numbers. I guess that is kind of implied by "optionally", though I wouldn't have phrased it that way. – Stephen C Sep 24 '10 at 13:19
  • 1
    I don't think the switch is significantly more readable, you have to look out for break statement and fall through. Very useful, but not as simple as "if". – NimChimpsky Sep 24 '10 at 13:25
  • 11
    @Rene Saarsoo It is best practice to have only one return statement – jmj Sep 24 '10 at 14:18
  • 58
    @org I hear that is best practice all the time, but when it makes the code awkward it seems silly to me. Do you have any substantiation of this claim that it's actually better? Note this is not the same as "it's consensus that this is best practice" but something proving that it IS best practice. – ErikE Sep 24 '10 at 17:14
  • 2
    I like the Javascript implementation of `switch` better: you can do `switch(true) {case size > 146: ... case size > 117: ...` – D'Arcy Rittich Sep 24 '10 at 17:39
  • 59
    @org.life.java This "best practice" is entirely subjective and most probably outdated (since it is a throwback to the days of languages like C where you have to explicitly manage resources). There are many times when you get far more readable and maintainable code by multiple return statements. Smart compilers take care of the rest. See http://consultingblogs.emc.com/anthonysteele/archive/2008/07/14/a-method-should-have-only-one-return-statement-discuss.aspx – Dan Diplo Sep 24 '10 at 19:00
  • 2
    What value is returned when `v == 9` ? – Stephen P Sep 24 '10 at 20:48
  • 2
    @RedFilter that's irrelevant. java's switch doesn't work that way, whether you like it or not... – Sean Patrick Floyd Sep 24 '10 at 23:34
  • @Stephen Obviously `size` is returned :D – Thomas Eding Sep 25 '10 at 03:58
  • 1
    @Dan it's not an issue with the compiler not understanding it, multiple return statements make code less understandable. – Sean Patrick Floyd Sep 25 '10 at 09:47
  • 7
    @seanizer: less understandable than using tons of useless intermediate steps for the sake of code "purity"? – ninjalj Sep 25 '10 at 10:32
  • 3
    @seanizer - It's *programmers* that make code readable or not readable, not rigid Stalanist orthodoxies that must be adhered to. – Dan Diplo Sep 25 '10 at 15:16
  • 3
    @dan *Stalanist*? Would you perhaps mean *Stalinist*? I can't remember murdering millions of innocent people any time recently, but then my memory isn't as good as it once was... @ninjalj if it takes many steps, that's an indication of poor code design. – Sean Patrick Floyd Sep 25 '10 at 15:36
  • @seanizer - Yeah, it was a typo, and yet it was a metaphor for sticking to some set-in-stone ideology rather than being pragmatic and using whichever approach is best suited to the situation. – Dan Diplo Sep 25 '10 at 16:07
  • 2
    @seanizer: your recommended way takes one step too many, saving the return result to a temporary variable. It gets even uglier when used inside loops. – ninjalj Sep 25 '10 at 20:19
  • @Emtucifor I am not saying that it is good or bad, but its better to keep a standard, I personally follow that, It will make it code more readable i feel. – jmj Sep 27 '10 at 08:22
  • 4
    @org I think that returning right away when that's what you intend makes the code more "readable." If we define readability as the speed with which the structure and intentions of the code are made apparent to the reader, then strategic returns make sense to me. The **expectation** that code will only return at the end of a procedure is the problem, and an artificial one that makes programming more laborious. Calling it a standard doesn't make it sensible. We could aLtErNaTe CaPs and call it a standard, but that wouldn't make it reasonable. – ErikE Sep 27 '10 at 16:07
  • 2
    If there are multiple natural exit points in some function (which should already not be many pages long or it's violating other useful principles), the work necessary to carry "desired exit state" to the end of the function makes everything more complex. It also makes it harder to ensure the function is performing correctly because all branches flow through the entire thing and some of the conditionals could be complex. In the code in this question, returns make perfect sense because nothing is done afterward! No one could POSSIBLY get confused about what's going on here. – ErikE Sep 27 '10 at 16:11
  • @Emtucifor, Here it will be readable but I am talking about complex flows, there I feel it should be one return statement. It will also help you while debugning. – jmj Sep 27 '10 at 16:15
  • 1
    @org Your statement "It is best practice to have only one return statement" was global. If you no longer think this is a global rule all should follow, well, more power to you. :) – ErikE Sep 27 '10 at 16:39
  • 1
    @Emtucifor : I Have looked @ http://stackoverflow.com/questions/36707/should-a-function-have-only-one-return-statement ,Found out more no of programmers prefer it, I would still say in complex flow I will stick with it/ – jmj Sep 27 '10 at 16:42
  • @org Best practice isn't the same as most common practice. I guess we just have to disagree. :) – ErikE Sep 27 '10 at 16:49
  • in favor of org.life.java statement, I'd say that even though his suggested "best practice" is subjective (note for bias: I adhere to it as well), it makes testing and debugging is easier by allowing you to monitor only one exit point (you'll say with some language/IDE combination, they manage function exit points just fine, but that's not always the same. So it's one of those "smallest common denominator" practice.) – haylem Oct 06 '10 at 10:31
  • @org.life.java: maybe, but that's the subjective part :) My main reason is "less code, less bug", and I prefer single exit points for traceability when adding logging statement (need only one instead of N, except if using AOP) or debugging. Plus in the end, most compilers would produce the same machine code, so optimization is not really a valid reason here. – haylem Oct 06 '10 at 10:48
  • 6
    [ONLY FOR THOSE WITH A SENSE OF HUMOR] For the purists out there, single exit points are incompatible with exception handling. Attempting to catch every possible exception would lead to catch-block code bloat and separate the return far away from the body of the method. For the sake of readability (aka single exit point), the code ends up unreadable(filled with catch blocks). The 'single exit point' approach is subjective. Just tell people "I prefer it that way" and go back to coding. No reasonable programmer will begrudge a preference when stated that way. – Kelly S. French Nov 30 '10 at 17:33
  • READABILITY on if..then..else construction in Java(!?) This question is perfect example of providing a piece of code as example and still managing to make a non-constructive/controversial/offtopic. I guess if you don't like if..then..else - do your own DSL development, if you don't know about existence of switch - learn Java keywords or rework the problem. I can only help by downvoting – Yauhen Yakimovich Nov 29 '12 at 23:03
51
return v > 145 ? 1 
     : v > 117 ? 2 
     : v > 68 ? 3 
     : v > 51 ? 4 
     : v > 22 ? 5 
     : v > 10 ? 6 
     : "put inital size value here";
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
dhblah
  • 9,751
  • 12
  • 56
  • 92
  • Are you for real? This is significantly more unreadable, and not trivial to change if the rules for 'size' change. – Noon Silk Sep 24 '10 at 11:09
  • 13
    could you please read initial question? "How can i get rid of multiple if statements?" There are no "if" statements in my answer as you can see. – dhblah Sep 24 '10 at 11:12
  • 12
    @gasan, sure, you removed the `if's` but the op claimed his code was `ugly`, and intended to make it more readable...do you actually believe this is...prettier? – st0le Sep 24 '10 at 11:18
  • 1
    The original question probably was meant to be "How can I get rid of multiple if statements without resorting to extremely ugly code" :-) – Grodriguez Sep 24 '10 at 11:19
  • 2
    It does technically answer the question on how to get rid of "if" statements, but it increases the "ugly" factor by a few orders of magnitude. – Greg Beech Sep 24 '10 at 11:19
  • 2
    Seen as just a row of characters without meaning, it is actually rather regular and predictable. Some would consider that to be pretty! – Thorbjørn Ravn Andersen Sep 24 '10 at 11:25
  • 6
    I added some newlines. Readability among starters is still questionable. – BalusC Sep 24 '10 at 11:36
  • 2
    @st0le, I think the code in the question is ugly and makes no sense thus the better solution is to make it as much small and compact as possible to not annoy one's eyes (Hopefully, some sane developer will remove it in the nearest future). Besides all, this function (probably, it is a function) should be inlined. And it's better to do it in a concise way rather than by humpty-dumpty many-lines-many-brackets code. – dhblah Sep 24 '10 at 11:41
  • 1
    @BalusC, Jesus Christ, what've you done with my code?! It was intended to be in a single line! (see why in my previous comment) – dhblah Sep 24 '10 at 11:48
  • 30
    You're confusing me with someone else. My name is Bauke Scholtz, not Jesus Christ. As said in a comment, I added some newlines. It is less ugly and improves readability. – BalusC Sep 24 '10 at 11:56
  • 3
    @BalusC Surely if there was ever code that justified taking the Lord's name in vain, this is it! – bzlm Sep 24 '10 at 12:38
  • 3
    @gasan: feel free though to rollback the edit so that you can eat downvotes :) Was just being helpful. – BalusC Sep 24 '10 at 12:48
  • 2
    haha yes! I love it, and it shouldn't be hard to read for anyone who knows the syntax :D – Kenny Cason Sep 24 '10 at 17:23
  • 12
    I like this form and find it readable, though I recognize that most people have a problem with it. I don't see why anyone who understands the chained `if/else-if/else` construct has a problem with the chained ternary `?:` operator. They are perfectly parallel to one another. – Bert F Sep 24 '10 at 17:40
  • 5
    If nothing else, this form forces you to write what the "default" case is. +1 – badp Sep 24 '10 at 19:23
  • 9
    I find this much easier to read than the accepted if/else-if answer, thanks to Balus' reformatting. I agree with Bert, yes, people seem to have a problem with this; I just don't understand *why* people find the ternary `?:` so cryptic. Agree with badp too, neither the OP's code or the accepted answer deal with a default "otherwise" case. – Stephen P Sep 24 '10 at 20:45
  • 2
    @Stephen P — I think ternary itself ends up seeming cryptic simply because it's farther from English than we usually like. But even I, being familiar with and probably overusing ternary syntax, get confused by figuring out how to process chained ternary. I might memorize that this is its behavior, though, for future reference. – Matchu Sep 25 '10 at 03:09
  • 9
    In the reformatted form this is nicer and more readable than a chain of if/elses. As for confusing people who don't understand the ternary operator -- well, anyone who doesn't understand ?: yet should jolly well learn it, and anyone who *can't* learn it clearly has deeper problems as a programmer. – Porculus Sep 25 '10 at 14:02
  • @porculus who put you in charge of deciding wbat a programmer should learn? The ternary operator is a nice gadget for an inline switch but it makes code less readable and imho this solution is a horrible abuse of it – Sean Patrick Floyd Sep 25 '10 at 20:01
  • 3
    @seanizer: Well, obviously a programmer should learn the whole language (at least the basics), there can be no debate about this. Further, this is a common idiom to deal with chained-ifs that merely select a value and far from being an abuse it’s a perfect use-case. I actually prefer *your* (apparently very controversial, though why eludes me) solution, but only barely. – Konrad Rudolph Sep 27 '10 at 16:50
  • 1
    I remember seeing a style rule somewhere that recommended not nesting ternaries. Can't remember where it was, though. – Powerlord Sep 27 '10 at 19:44
23

The original code seems fine to me, but if you don't mind multiple returns you might prefer a more tabular approach:

if ( v > 145 ) return 1;
if ( v > 117 ) return 2;
if ( v >  68 ) return 3;
if ( v >  51 ) return 4;
if ( v >  22 ) return 5;
if ( v >  10 ) return 6;
return ...;     // The <= 10 case isn't handled in the original code snippet. 

See the multiple return or not discussion in org.life.java's answer.

Community
  • 1
  • 1
Jim Ferrans
  • 30,582
  • 12
  • 56
  • 83
17

There are a ton of answers and suggestions here but I honestly don't see any of them "prettier" or "more elegant" than the original method.

If you had dozens or HUNDREDS of iterations to check then I could easily see going to some for loop but honestly, for the handful of comparisons you had, stick with the if's and move on. It's not that ugly.

cbmeeks
  • 11,248
  • 22
  • 85
  • 136
  • 6
    Agree. But these questions let people show alternate solutions. For example, I had never heard of NavigableMap. – Tony Ennis Sep 24 '10 at 14:21
  • Well you probably heard of a TreeMap, but you didn't know which interfaces it implements :-) – Sean Patrick Floyd Sep 24 '10 at 15:20
  • 1
    Agreed… Take a good, hard look at your first revision and just say to yourself, [gloves]. [gloves]: http://thedailywtf.com/Articles/The_Complicator_0x27_s_Gloves.aspx *Edit:* Why does this choke the Markdown parser?! – Left For Archive Sep 25 '10 at 01:15
14
return (v-173) / -27;
mhaller
  • 14,122
  • 1
  • 42
  • 61
12

Here's my shot at it...

Update: Fixed. Previous Solution gave incorrect answers for exact values (10,22,51...). This one defaults to 6 for the if val < 10

   static int Foo(int val)
    {
                          //6, 5, 4, 3, 2 ,1
        int[] v = new int[]{10,22,51,68,117,145};
        int pos = Arrays.binarySearch(v, val-1);
        if ( pos < 0) pos = ~pos;
        if ( pos > 0) pos --;
        return 6-pos;
    }
st0le
  • 33,375
  • 8
  • 89
  • 89
  • I was just about posting a solution like yours, using `binarySearch`. +1 for using the `~` operator on `binarySearch` result, I never thought about that. Note : you can get rid of the `size` array, it's not 5,6 but 6,5. – barjak Sep 24 '10 at 12:57
  • 1
    @LnxPrgr3, i agree. It's not, was just offering an alternative. – st0le Sep 24 '10 at 19:37
  • +1 for a solution that works quickly for large datasets, and doesn't have more overhead than the original for small datasets. – Thomas Ahle Oct 15 '10 at 15:26
11

I have one more version for you. I don't really think it's the best one because it adds unnecessary complexity in the name of "performance" when I'm 100% sure this function will never be a performance hog (unless someone is calculating size in a tight loop a million times ...).

But I present it just because I thought performing a hard-coded binary search to be sort of interesting. It doesn't look very binary-y because there aren't enough elements to go very deep, but it does have the virtue that it returns a result in no more than 3 tests rather than 6 as in the original post. The return statements are also in order by size which would help with understanding and/or modification.

if (v > 68) {
   if (v > 145) {
      return 1
   } else if (v > 117) {
      return 2;
   } else {
      return 3;
   }
} else {
   if (v > 51) {
      return 4;
   } else if (v > 22) {
      return 5;
   } else {
      return 6;
   }
}
ErikE
  • 48,881
  • 23
  • 151
  • 196
7
7 - (x>10 + x>22 + x>51 + x>68 + x>117 + x>145)

where 7 is the default value (x <= 10).

Edit: Initially I didn't realize this question is about Java. This expression is not valid in Java, but is valid in C/C++. I will leave the answer, as some users found it helpful.

George
  • 3,765
  • 21
  • 25
5

Here is an object-oriented solution, a class called Mapper<S,T> that maps values from any type that implements comparable to any target type.

Syntax:

Mapper<String, Integer> mapper = Mapper.from("a","b","c").to(1,2,3);

// Map a single value
System.out.println(mapper.map("beef")); // 2

// Map a Collection of values
System.out.println(mapper.mapAll(
    Arrays.asList("apples","beef","lobster"))); // [1, 2, 3]

Code:

public class Mapper<S extends Comparable<S>, T> {

    private final S[] source;
    private final T[] target;

    // Builder to enable from... to... syntax and
    // to make Mapper immutable
    public static class Builder<S2 extends Comparable<S2>> {
        private final S2[] data;
        private Builder(final S2[] data){
            this.data = data;
        }
        public <T2> Mapper<S2, T2> to(final T2... target){
            return new Mapper<S2, T2>(this.data, target);
        }
    }


    private Mapper(final S[] source, final T[] target){
        final S[] copy = Arrays.copyOf(source, source.length);
        Arrays.sort(copy);
        this.source = copy;
        this.target = Arrays.copyOf(target, target.length);
    }

    // Factory method to get builder
    public static <U extends Comparable<U>, V> Builder<U> from(final U... items){
        return new Builder<U>(items);
    }

    // Map a collection of items
    public Collection<T> mapAll(final Collection<? extends S> input){
        final Collection<T> output = new ArrayList<T>(input.size());
        for(final S s : input){
            output.add(this.map(s));
        }
        return output;
    }

    // map a single item
    public T map(final S input){
        final int sourceOffset = Arrays.binarySearch(this.source, input);
        return this.target[
            Math.min(
                this.target.length-1,
                sourceOffset < 0 ? Math.abs(sourceOffset)-2:sourceOffset
            )
        ];
    }
}

Edit: finally replaced the map() method with a more efficient (and shorter) version. I know: a version that searches partitions would still be faster for large arrays, but sorry: I'm too lazy.

If you think this is too bloated, consider this:

  1. It contains a builder that lets you create the Mapper using varargs syntax. I'd say that's a must-have for usability
  2. It contains both a single item and a collection mapping method
  3. It's immutable and hence thread safe

Sure, all of these features could be easily removed, but the code would be less complete, less usable or less stable.

Tim Post
  • 33,371
  • 15
  • 110
  • 174
Sean Patrick Floyd
  • 292,901
  • 67
  • 465
  • 588
  • 16
    Too complicated.... why should someone involve objects in something as simple as take decisions over primitive types? – JPCF Sep 24 '10 at 16:07
  • This is meant as a general-purpose solution. In this simple case it may be overhead, but in more advanced use cases (staring with the last example I provided) the benefits of utilizing the comparable interface are huge. – Sean Patrick Floyd Sep 24 '10 at 16:12
  • 1
    Of course it would be even better if you could pass in a custom comparator, maybe I'll rework it to that. – Sean Patrick Floyd Sep 24 '10 at 16:14
  • 1
    How is this an improvement over the [NavigableMap-based answer](http://stackoverflow.com/questions/3786358/get-rid-of-ugly-if-statements/3786541#3786541)? At the very least, if I wanted this interface, I would have wrapped NavigableMap before writing anything like this. I'd be interested to see a benchmark of your solution vs. NavigableMap too. – Left For Archive Sep 24 '10 at 16:54
  • 31
    Holy Grace Hopper, what monstrosity is this? They just wanted to tighten up a few IFs. – Incognito Sep 24 '10 at 18:08
  • @LnxPrgr3 losing a benchmark against a standard library used by millions of users ain't such a shame, is it? Let's see your wrapper around NavigableMap... @user257493 imho 6 is not a few. If there are more than 2 if clauses, refactoring is a must. and if you don't like object-oriented solutions, maybe you should stick with a strictly procedural language like c. btw, who are you to take grace hopper's name in vain? – Sean Patrick Floyd Sep 24 '10 at 23:23
  • 10
    @seanizer Premature abstraction is the root of all evil. No… losing a benchmark to the standard library isn't a shame. Rolling your own solution *anyway* without gaining *something* over the standard library is an odd choice though, especially when it means replacing 6 `if` statements with 54 lines of Java (not counting the code to actually *use* this thing). Besides, when was being object oriented inherently an advantage? – Left For Archive Sep 25 '10 at 00:59
  • @Lnx if you don't think being object oriented is inherently an advantage, maybe you should stick with a non-oo language. Java is not c sans pointers, it's an object oriented language (even if it's not a clean one). object orientation gains nothing at the machine level, but it makes code readable and reusable and reduces bugs because it's easily testable. Also: If 10 similar situations occur in a given code base, then my solution can be reused as a oneliner, while if/else based solutions would have to be repeated entirely. – Sean Patrick Floyd Sep 25 '10 at 09:43
  • 1
    BTW I strongly disagree with your point about abstraction. Abstraction is what makes code manageable. – Sean Patrick Floyd Sep 25 '10 at 09:45
  • 3
    @seanizer Do you seriously believe this is more maintainable than the original? I'm all for OOP when it makes sense, but the chain of if statements was already easy to read and modify. – Left For Archive Sep 25 '10 at 16:29
  • @Lnx Yes, just as I would prefer an ArrayList over an array. This is a class you can define once and use it for many different purposes. It's immutable, thread safe and reasonably fast, while being extensible and reusable. – Sean Patrick Floyd Sep 25 '10 at 16:42
  • 4
    Intuition reading your code without reading the Mapper class would make me think 10 maps to 6, etc., which it does not. If I ran into this in someone's codebase, I'd have to read and comprehend your class (or its documentation, had you bothered to write any) to know what this really does. This takes longer than understanding a chain of if statements. Sure, your solution is more general and even clever, but who said either was needed in this context? – Left For Archive Sep 26 '10 at 00:53
  • 1
    Even if you were to include a custom comparator, it still wouldn't be clear whether your mapper will "round" up or down to the thresholds. You have to go and look inside the class. Given that the > operator has been thoroughly tested, I'm betting your coworkers would rather use that. Also, there's no indication that your abstraction is even right. You've hard-coded the default case, and the > relationship, while at the same time assuming that the OP "might" need to put in strings at a later date. Given the variable name "size", that just seems like an odd assumption to make. – Chris Burt-Brown Sep 26 '10 at 01:17
  • Although I will agree with you that other answers here are also worth the downvote. – Chris Burt-Brown Sep 26 '10 at 01:18
  • 1
    @lnx a solution like this relies on documentation, that's clear. 2 lines of javadoc and every fellow worker would immediately see what this is about, without even leaving the IDE. @chris I am not saying others deserve a downvote, quite the contrary. I am saying when someone takes the time to elaborate a working solution they deserve some respect cos they are the people who keep this site alive. My own policy is this: upvote what I like, downvote crap only. And whether you like my solution or not, it definitely is not crap. It may be overkill, but it works – Sean Patrick Floyd Sep 26 '10 at 06:11
  • Does it? Even after importing `java.util.*` your code fails to compile on this line in the constructor for Mapper: `final U[] copy = Arrays.copyOf(source, source.length);` – Left For Archive Sep 26 '10 at 07:35
  • Sorry I refactored the code in the browser earlier and apparently forgot to change the name of a type variable. It should qork now, just as the first version worked (but I have no way of testing it right now as there's no jdk on my iphone) – Sean Patrick Floyd Sep 26 '10 at 09:00
  • Work, not qork. Although it might also qork. I'll test that when I get home. Btw including import statements in SO listings is considered unnecessary noise in obvious cases like this one. – Sean Patrick Floyd Sep 26 '10 at 09:07
  • 2
    This compiles, but has unexpected results: `Mapper map = Mapper.from(145, 117, 68, 51, 22, 10).to(1, 2, 3, 4, 5, 6); System.out.println(map.mapAll(Arrays.asList(11, 23, 52, 69, 118, 146)));` returns `[1, 2, 3, 4, 5, 6]` where I'd expect quite the opposite. This would be much easier to spot and fix in a chain of if statements. – Left For Archive Sep 26 '10 at 10:00
  • Unexpected? Since Comparable is used internally, every other behavior would be nonsense and potentially undecidable. E.g. If you asked the program to map 10,30,50,20,40 to a, b, c, d and e what are going to do with input value 25? You have to sort the input first to successfully use comparable. And of course one could extend it easily to reverse sort order something like `Mapper.descending().from(..).to(..)`, but I`d prefer to sort my input data, as it's the more intuitive way to do things. – Sean Patrick Floyd Sep 26 '10 at 12:19
  • What's obvious is that this solution relies on documentation, but that's common to all solutions here. (I assume the requested mapping will be used in more than one place and hence will be stored in some static method in the if/else case just as it will be stored in an instance variable or a singleton in the case of my solution.) – Sean Patrick Floyd Sep 26 '10 at 12:29
  • What are you talking about? Notice all I did was flip both arrays… so it should provide the same output as your example. I notice you sort the input array, but not the output array… it looks like you intended to allow the conditions to be specified out of order, but your class does not support this. Because of this, I can't write the conditions in the natural order they would be written in a chain of if statements. If this is a feature and not a bug, then this class *really* needs documentation, which I notice has not been written. – Left For Archive Sep 26 '10 at 19:21
  • A better approach would be to use a sorted map of some variety in the first place. This would allow you to sort the values to match the keys, and then users of your class can provide input in arbitrary order. At the very least, this should support writing the conditions in their natural order (`if (v > 145) /* ... */; else if (v > 117) /* ... */`), since the if statement is at least this flexible. – Left For Archive Sep 26 '10 at 19:33
  • @Lnx Of course no doc has been written, this is no software project of mine, but a quick solution I hacked up in 20 minutes as a tech demo. Sorting the input array is necessary, as I said (and consistent with the NavigableMap version), but sorting the output array would limit the possibilities of this version. "Natural Order" in Java means the order created by the Comparable interface (read http://download.oracle.com/javase/6/docs/api/java/lang/Comparable.html) and hence exactly what I implemented. And it's more intuitive, too. I don't know about you, but I count 1, 2, 3 not 3, 2, 1. – Sean Patrick Floyd Sep 27 '10 at 04:48
  • 4
    I am not saying this is the solution everybody should use, but it's a valid approach and certainly more original than the if / else boredom we all learned 30 or 40 years ago. All I'm saying is: there's more than one way to do it, and coming from an OOP and somewhat functional background this is the kind of solution I favor. – Sean Patrick Floyd Sep 27 '10 at 04:55
  • 7
    Your answer has earned kind of award: http://forums.thedailywtf.com/forums/p/20192/234306.aspx – BalusC Sep 27 '10 at 14:30
  • 1
    kewl. fortune and fame, finally. :-) – Sean Patrick Floyd Sep 27 '10 at 14:44
  • 1
    Strangely, reputation has gone *up* since the WTF article. Probably not what the author had in mind. Thanks very much, "Enterprise Architect". – Sean Patrick Floyd Sep 27 '10 at 14:50
  • We cannot easily say whether this answer is an overkill or not because the OP does not mention where he needs this type of code. All these magic numbers (in the OP's question) make me wonder where is this used. – cherouvim Sep 27 '10 at 16:18
  • 1
    Exactly. I *assumed* this was a mapping of importance that would be used in several places and have therefor designed a simple reusable solution that can be dependency injected all over the place. If it's a one time thing then yes, of course it's overkill. – Sean Patrick Floyd Sep 27 '10 at 16:30
  • if you wanted to beat the TreeMap, you should have sorted the arrays and use binary search... – fortran Sep 28 '10 at 09:30
  • I did not want to *beat the TreeMap*. The TreeMap is a standard library element that's been under development for years. My code was written in 20 minutes. I'd have to be Jon Skeet to win that battle. That said, yes of course binary search would be a good idea. – Sean Patrick Floyd Sep 28 '10 at 11:53
  • @Epaga there have been enough comments of people who actually *liked* my solution, both on this page and on the WTF thread that I think I can live both with that possibility and with your own sarcastic downvote. – Sean Patrick Floyd Sep 29 '10 at 07:01
  • @seanizer sure...and it's all meant in good spirit. for me it's just a great example of enterprise programming where we take something that could be done far faster, far more readably, and far simpler without OOP and morph it into an abstract OOP monster. sometimes that might be needed, but my experience is those times are few and far between. – Epaga Sep 29 '10 at 09:59
  • @Epaga *good spirit*? Like this maybe? http://www.flickr.com/photos/gillyberlin/4931194595/ Whatever. For the x + nth time: I was just trying to make the picture complete by adding an object oriented solution. At that time, several good solutions were already present, some of which I had praised and upvoted, so I never said mine is the one that *should* be used. The question was to get rid of ugly if statements and that is exactly what I did, at the cost of efficiency. It was an experiment, nothing more. BTW: I'd say my calling syntax is way more readable than a multi if/else solution. – Sean Patrick Floyd Sep 29 '10 at 10:21
  • 1
    If you have a hammer, everything looks like a nail. I feel sorry for whoever would have to read through that code just to replicate the functionality of an if-statement chain. – Lotus Notes Sep 29 '10 at 19:22
  • 1
    @Lotus when I look at your user page I see PHP, PHP and even more PHP. Perhaps you should stick with topics that better fit your "Skilset". Sorry, I'm starting to get really p***ed off by people like yourself who dump their bad karma on me. You don't like my code? Fine. You downvote it? Fine. But saying you feel sorry for people who have to read someone's code is unnecessary and f***ing offensive. – Sean Patrick Floyd Sep 29 '10 at 19:36
  • 1
    @seanizer If PHP was really my best skillset, why would I be asking so many questions about it? My main skillset (and career) is Java. For the record, PHP has all the object-oriented features that you need. It's quite ignorant to make a blanket statement about an entire language simply because it doesn't **enforce** OOP. My intent wasn't to be offensive, but rather to be practical. I never said that your code was bad, I simply said that it's an unnecessary burden for any co-worker who would have to read that. In my opinion, managers wouldnt be happy with someone going overboard on abstraction. – Lotus Notes Sep 29 '10 at 19:53
  • a) For the record: I used PHP for about 10 years, I'm still recovering. Why would anyone intentionally use PHP if they know Java? Why on earth not Ruby or Python which are now almost equally available everywhere? b) I said if anything they should use it, not read it (when was the last time you read the PHP or Java compiler's code for parsing if statements?) c) Last sentence: agreed, and rightly so. There are a few situations where this solution of mine could solve problems, but in most situations it would be cludgy (and I never said otherwise). I still think my code is prettier than long if... – Sean Patrick Floyd Sep 29 '10 at 20:02
  • .../else chains but we all know they work so I also use them more often then I like to. I'm not the OOP-maniac everybody seems to think I am, I was just interested in making a technology demonstration. – Sean Patrick Floyd Sep 29 '10 at 20:05
  • 1
    @seanizer sorry if my downvote or comment offended you. i wouldn't take downvotes so personal if i were you. this is your style of code. it seems that about 50% of SO users disagree with you. that could be a lot worse. it's kind of a glass-half-full thing I suppose. ;-) – Epaga Sep 30 '10 at 07:46
  • and about this statement: *it seems that about 50% of SO users disagree with you*. Most of the flaming commentators above have little to no activity in the java tags, so I am guessing it will be the same for the downvoters. I have not received criticism (much less flaming) from *anyone* with at least a bronze java badge while those who liked my solution are mostly very active in the Java tag. So while it's probably more than 95% of SO users that find my solution awful, the experienced java guys apparently don't and that's what counts for me. – Sean Patrick Floyd Sep 30 '10 at 09:31
  • 2
    @seanizer dude now you're the one getting personal. chill. I was not asking people to ridicule you - but since that's the way you took it, I just deleted the comment and I apologize it came across that way. – Epaga Oct 01 '10 at 07:52
  • 2
    OK, mine is gone too. Maybe it's about time someone closed this thread. – Sean Patrick Floyd Oct 01 '10 at 08:01
  • 1
    @Sean Patrick Floyd, i think you have a bug in ` Math.min( this.target.length-1, sourceOffset < 0 ? Math.abs(sourceOffset)-2:sourceOffset ) ` – bestsss Feb 28 '11 at 13:22
  • @bestsss I believe you but I'm not going to touch this old beast any more. I was never serious about it in the first place, but I got attacked big time and was made to defend it. Now I would be happy to just let it die. It passed several simple tests back then, but it's far from production quality :-) – Sean Patrick Floyd Feb 28 '11 at 14:06
  • 1
    @Sean, why, the core idea of binary search is fine w/ me :). If the sourceOffset is -1 effectively you have it as index Math.abs(-1)-2==-1. I guess you want `~sourceOffset`. please, edit, it's a nice piece of code – bestsss Feb 28 '11 at 14:16
5

My commenting ability isn't turned on yet, hopefully no one will say "rightfully" based on my answer...

Pretty-ing up the ugly code could/should be defined as trying to achieve:

  1. Readability (OK, stating the obvious -- redundant to the question perhaps)
  2. Performance -- at best seeking optimal, at worst it's not a big drain
  3. Pragmatism -- it's not far off the way most people do things, given an ordinary problem that's not in need of an elegant or unique solution, changing it later on should be a natural effort, not in need of much recollection.

IMO the answer given by org.life.java was the prettiest and extremely easy to read. I also liked the order in which the conditions were written, for reasons of reading and performance.

Looking over all the comments on this subject, at the time of my writing, it appears that only org.life.java raised the issue of performance (and maybe mfloryan, too, stating something would be "longer"). Certainly in most situations, and given this example it shouldn't bear a noticeable slowdown however you write it.

However, by nesting your conditions and optimally ordering the conditions can improve performance [worthwhile, particularly if this were looped].

All that being said, nesting and ordering conditions (that are more complex than your example) brought on by determination to achieve as fast as possible execution will often produce less readable code, and code that's harder to change. I refer again to #3, pragmatism... balancing the needs.

Chris Adragna
  • 625
  • 8
  • 18
4

Is there an underlying mathematical rule to this? If so you should use that: but only if it comes from the problem domain, not just some formula that happens to fit the cases.

user207421
  • 305,947
  • 44
  • 307
  • 483
3
int[] arr = new int[] {145, 117, 68, 51, 22, 10};
for(int index = 0; index < arr.length; index++)
{
  if(v > arr[index]) return 1 + index; 
}

return defaultValue;
Ani
  • 111,048
  • 26
  • 262
  • 307
3

You could rewrite it in ARM code. It's only 7 cycles worst case and a slender 164 bytes. Hope that helps. (note: this is untested)

; On entry
;   r0 - undefined
;   r1 - value to test
;   lr - return address
; On exit
;   r0 - new value or preserved
;   r1 - corrupted
;
wtf
        SUBS    r1, r1, #10
        MOVLE   pc, lr
        CMP     r1, #135
        MOVGT   r0, #1
        ADRLE   r0, lut
        LDRLEB  r0, [r0, r1]
        MOV     pc, lr
;
; Look-up-table
lut
        DCB     0   ; padding
        DCB     6   ; r1 = 11 on entry
        DCB     6
        DCB     6
        DCB     6
        DCB     6
        DCB     6
        DCB     6
        DCB     6
        DCB     6
        DCB     6
        DCB     6
        DCB     6
        DCB     5   ; r1 = 23 on entry
        DCB     5
        ...
        ALIGN
2

Actually, if the sizes are likely to change, doing it in the database could be a good alternate strategy:

CREATE TABLE VSize (
   LowerBound int NOT NULL CONSTRAINT PK_VSize PRIMARY KEY CLUSTERED,
   Size int NOT NULL
)
INSERT VSize VALUES (10, 6)
INSERT VSize VALUES (22, 5)
INSERT VSize VALUES (51, 4)
INSERT VSize VALUES (68, 3)
INSERT VSize VALUES (117, 2)
INSERT VSize VALUES (145, 1)

And a stored procedure or function:

CREATE PROCEDURE VSizeLookup
   @V int,
   @Size int OUT
AS
SELECT TOP 1 @Size = Size
FROM VSize
WHERE @V > LowerBound
ORDER BY LowerBound
ErikE
  • 48,881
  • 23
  • 151
  • 196
1

Just for completeness, let me suggest that you could set up an array SIZES with 145 elements so the answer could be returned directly as SIZES[v]. Pardon me for not writing the whole thing out. You would have to make sure v was in range, of course.

The only reason I can think of for doing it that way would be if you were going to create the array once and use it thousands of time in an application that had to be really fast. I mention it as an example of a trade-off between memory and speed (not the problem it once was), and also between setup time and speed.

phv3773
  • 487
  • 4
  • 10
  • This is the same as my solution below that got -1 for whatever reason even though it is a very reasonable situation for some situations. – CashCow Oct 11 '10 at 14:12
1

The obvious answer is to use Groovy:

def size = { v -> [145,117,68,51,22,10].inject(1) { s, t -> v > t ? s : s + 1 } }

One liners are always better. Returns 7 for the undefined case where v <= 10.

Zutty
  • 19
  • 1
0

why somebody have not suggested switch statement. it is far better then if else ladder.

public int getSize(int input)
    {
        int size = 0;
        switch(input)
        {
        case 10:
            size = 6;
            break;

        case 22:
            size = 5;
            break;


        case 51:
            size = 4;
            break;

        case 68:
            size = 3;
            break;

        case 117:
            size = 2;
            break;

        case 145:
            size = 1;
            break;
        }

        return size;
    }
RTA
  • 1,251
  • 2
  • 14
  • 33
  • Because the condition is "greater than". For 100 the original code returns 3 but yours return 0 – nrofis Oct 17 '20 at 14:16
0

This is my code sample, using SortedSet. You initialise boundaries once.

SortedSet<Integer> boundaries = new SortedSet<Integer>;

boundaries.add(10);

boundaries.add(22);

boundaries.add(51);

boundaries.add(68);

boundaries.add(117);

boundaries.add(145);

Then use it subsequently this way for multiple values of v (and initialised size)

SortedSet<Integer> subset =  boundaries.tailSet(v);
if( subset.size() != boundaries.size() )
  size = subset.size() + 1;
CashCow
  • 30,981
  • 5
  • 61
  • 92
  • 1
    This is intended to show a generic way to make a "histogram" type lookup, and uses an O(log N) lookup for the number of boundaries. Where N is small this may prove to be less efficient than nested if statements. – CashCow Sep 27 '10 at 14:51
  • First of all: this won't compile. It must be `SortedSet`, not `SortedSet`. Also this approach has already been taken more effectively using `NavigableMap.lowerEntry()` here: http://stackoverflow.com/questions/3786358/get-rid-of-ugly-if-statements/3786541#3786541 – Sean Patrick Floyd Sep 27 '10 at 15:29
  • No, NavigableMap is different because you had to associate the value with the entry. Now what do you think would happen if they decided to insert 89 as an extra boundary and we now return 1..7. I would require one extra add() whilst NavigableMap would require changing the values on all the lower existing ones. – CashCow Sep 27 '10 at 16:04
  • Yup, I know, my own solution is the most flexible one :-) – Sean Patrick Floyd Sep 27 '10 at 16:35
  • @seanizer: Your solution is indeed the most flexible one, but if the intention of the OP is that of what Neil thinks it is (count the # of boundaries above value v) his solution would be more correct. – Alexander Torstling Sep 28 '10 at 07:21
  • @disown True. There is never a perfect solution for all cases. The question is whether you want to make a perfect solution for a tiny number of cases or good solution that matches many cases. In a performance-critical part of your application you'll take the first approach, but if you're going to have to meet changing requirements (and don't they always change?) then a flexible solution is more appropriate. – Sean Patrick Floyd Sep 28 '10 at 07:36
  • @seanizer: I think that the real problem is that you cannot read out the original problem statement from a general solution. This leads to a lot of solutions to non-existent problems, trying to keep the flexibility of the first solution. We simply don't know if the sequence 6..1 is arbitrary or not, so why not just ask the OP? If you have changing requirements, chances are they change something which isn't reflected in your 'semi-general' solution, so you are better of making a very specific solution. – Alexander Torstling Sep 28 '10 at 08:26
  • a) At the time I wrote my solution, there were about 8 answers, one of which was already accepted. I didn't think the OP would still be participating in the discussion. b) all the other good versions were already there, my approach was to point out a different and unusual approach to make the picture complete. c) yes, I was making some assumptions and I think I stated that I did. Remember, the OP is not my client, he's a guy who asked a question on SO. He got good answers and bad answers but first of all he got free answers from people who regularly charge loads of money for their services. – Sean Patrick Floyd Sep 28 '10 at 08:42
  • I don't think that implies the kind of relationship a developer has with a client, do you? – Sean Patrick Floyd Sep 28 '10 at 08:44
  • @seanizer: I don't mind people posting solutions, and I didn't think that your solution was bad, I'm just saying that the guess of Neal (1..6 won't change and the problem is 'count boundaries above') is just as good as yours (1..6 will change and the problem is 'map number to integer interval'). You both posted working solutions, and both are good, but none is better than the other, since we simply don't know what the original problem was. – Alexander Torstling Sep 28 '10 at 08:54
  • @disown sure, but you forget the context: my solution was WTFed, flamed and downvoted, others weren't, while I respected other solutions and upvoted several of them. So when I get some yahoos calling my code a monstrosity, I at least reserve to point out the weaknesses of other solutions, even if it's not Neil's fault. I think this solution is short-sighted and I am allergic to that kind because short-sighted solutions from other developers have caused me hundreds of work hours in the past. That's why I don't subscribe to the YAGNI philosophy, I've seen it fail too often. – Sean Patrick Floyd Sep 28 '10 at 09:15
  • @seanizer: I don't subscribe to YAGNI in that sense either, I do believe that it is warranted to prepare for 'any change' if it is possible to do so (like using data models, strategy patterns etc). But I'm against preparing for non-general change like 'maybe someone will want to change the layout of this button so I'll stick the dimensions in a property file'. Of course it's a delicate balance trying to decide when to introduce general flexibility, which is why I think YAGNI is phrased as it is now. – Alexander Torstling Sep 28 '10 at 10:32
0

If you really want the fastest big-O complexity time solution for this particular answer this one is constant lookup.

final int minBoundary = 10;
final int maxBoundary = 145;
final int maxSize = 6;
Vector<Integer> index = new Vector<Integer>(maxBoundary);
    // run through once and set the values in your index

subsequently

if( v > minBoundary )
{
   size = (v > maxBoundary ) ? maxSize : index[v];
}

What we are doing here is marking all the possible results of v within the range and where they fall, and then we only need to test for boundary conditions.

The issue with this is that it uses more memory and of course if maxBoundary is a lot bigger it will be very space inefficient (as well as take a longer time to initialise).

This may sometimes be the best solution for the situation.

CashCow
  • 30,981
  • 5
  • 61
  • 92
0

It is interesting that there are plenty of beautiful answers for a simple "ugly" question. I like mfloryan's answer best, however I would push it further by removing the hard-coded array inside the method. Something like,

int getIndex(int v, int[] descArray) {
    for(int i = 0; i < descArray.length; i++)
        if(v > descArray[i]) return i + 1;
    return 0;
}

It now becomes more flexible and can process any given array in descending order and the method will find the index where the value 'v' belongs.

PS. I cannot comment yet on the answers.

Adrian M
  • 7,047
  • 7
  • 24
  • 26
-1
            if (v <= 10)
                return size;
            else {
                size = 1;

                if (v > 145)
                    return size;
                else if (v > 117)
                    return ++size;
                else if (v > 68)
                    return (size+2);
                else if (v > 51)
                    return (size+3);
                else if (v > 22)
                    return (size+4);
                else if (v > 10)
                    return (size+5);
            }

This will execute the necessary if statements only.

Vikram
  • 3,996
  • 8
  • 37
  • 58
-1

Yet another variation (less pronounced than the answer by George)

  //int v = 9;
  int[] arr = {145, 117, 68, 51, 22, 10};
  int size = 7; for(;7 - size < arr.length && v - arr[size - 2] > 0; size--) {};
  return size;
Community
  • 1
  • 1
Yauhen Yakimovich
  • 13,635
  • 8
  • 60
  • 67