Programming Puzzles & Code Golf Stack Exchange is a question and answer site for programming puzzle enthusiasts and code golfers. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

Note! Unfortunately, I had to adjust the bounds an hour after posting this. If you posted before then, see the tweaked numbers!


Problem:

Your task is to write a program that takes as input a height (in meters) and weight (in kilograms), and outputs the corresponding BMI category.

BMI is a measure of the ratio of your weight to your height. It's dated and inaccurate for many people, but that doesn't matter here!

BMI can be calculated using the following equation:

BMI = (mass in kilograms) / (height in meters)^2

The categories will be defined as follows:

  • BMI < 18.5: "Underweight"

  • 18.5 <= BMI < 25: "Normal"

  • 25 <= BMI: "Overweight"

For the sake of the challenge, I'm ignoring all the "extreme" categories. Also, since some numbers like "25" sit between 2 categories, I adjusted the bounds slightly so there's a definite answer.

You can write either a function, or a full program.

Input:

Input can be in any reasonable form. Two numbers (or strings), either as 2 separate arguments, or as a single string. An array/list of 2 numbers, a dictionary with "weight" and "height" keys... Decimal values should be supported. You can assume the input will always be valid (no negative values, and height will never be 0).

Output:

Output will be a string containing the case-insensitive category names. The strings must match the category names exactly as above, ignoring case. It can be output to the stdout, returned (in the case of a function), or written to file.

Test Cases (weight, height => result):

80, 1 =>   "Overweight"
80, 2 =>   "Normal"
80, 3 =>   "Underweight"

50, 1 =>   "Overweight"
50, 1.5 => "Normal"
50, 2 =>   "Underweight"

Edge Cases:

41, 1.5 => "Underweight" (18.2 BMI)
42, 1.5 => "Normal" (18.667 BMI)

56, 1.5 => "Normal" (24.889 BMI)
57, 1.5 => "Overweight" (25.3 BMI)

73, 2 =>   "Underweight" (18.25 BMI)
74, 2 =>   "Normal" (18.5 BMI)

99, 2 =>  "Normal" (24.75 BMI)
100, 2 => "Overweight" (25 BMI)

Here's some pseudocode that shows an example implementation:

function bmi_category(weight, height):
    var bmi = (weight / (height**2))

    if (bmi < 18.5):
        return "Underweight"

    if (18.5 <= bmi < 25):
        return "Normal"

    if (25 <= bmi):
        return "Overweight"

This is code-golf, so the fewest number of bytes wins.

(Yes, this task is exceedingly trivial in most languages. Most of the challenges lately seem to be harder than normal, so I thought I'd post a more accessible one).


NOTE! An hour after I posted this challenge, I had to modify the ranges slightly since the ranges as stated had "holes" as pointed out in the comments. Please see the new ranges.

share|improve this question

27 Answers 27

Python, 69 bytes

lambda w,h:["UOnvd"[w/h/h>20::2]+"erweight","Normal"][18.5<=w/h/h<25]

Try it online!

Compare to 72 bytes:

lambda w,h:"Underweight"*(w/h/h<18.5)or"Normal"*(w/h/h<25)or"Overweight"
share|improve this answer

TI-Basic, 58 54 bytes

Input 
X/Y²→C
"NORMAL
If 2C≤37
"UNDERWEIGHT
If C≥26
"OVERWEIGHT

Also, for fun, here's a more compact version that is more bytes:

Prompt A,B
sub("UNDERWEIGHTNORMAL      OVERWEIGHT ",sum(A/B²≥{18.5,25})11+1,11

All I can say is, thank you for making this case-insensitive ;)

P.S. Input takes graph input to X and Y similar to Prompt X,Y

share|improve this answer
    
Also, I've looked at this multiple times, and I don't think there's any way to save bytes even though the last two share the string ERWEIGHT – Timtech yesterday
    
Out of curiosity, how would it have changed your answer if I enforced case-sensitivity? – Carcigenicate 3 hours ago

Jelly, 24 bytes

÷÷⁹Ḥ“%2‘>Sị“$⁽¿“;ṅẒ“&ċ)»

Try it online!

How?

Calculates the BMI, doubles it, compares that with the greater than operator with each of the numbers 37 and 50 (18.5 and 25 doubled), sums the resulting ones and zeros (yielding 1, 2, or 0 for Normal, Underweight, and Overweight respectively) and indexes into the list of strings ["Normal","Underweight","Overweight"].

÷÷⁹Ḥ“%2‘>Sị“$⁽¿“;ṅẒ“&ċ)» - Main link: weight, height
÷                        - weight ÷ height
  ⁹                      - right argument, height
 ÷                       - ÷ by height again to get the BMI
   Ḥ                     - double the BMI
    “%2‘                 - list of code page indexes [37,50]
        >                - greater than? (vectorises) - i.e [18.5>bmi, 25>bmi]
         S               - sum -- both:=2 (Underweight), just 50:=1 (Normal) or neither:=0 (Overweight)
          ị              - index into (1-based)
           “$⁽¿“;ṅẒ“&ċ)» - compressed list of strings ["Normal","Underweight","Overweight"]
                         - implicit print
share|improve this answer
    
Wow. Talk about obfuscation. If this is golf, I think you got a hole in one! Or 24... – cullub 16 hours ago
2  
@cullub it is 24 characters and 24 bytes - Jelly uses it's own code-page linked to by the word "bytes" in the heading, mothereff.in is counting Unicode I believe. – Jonathan Allan 15 hours ago
1  
@TheBitByte, yes, take a look at the TIO link counter "24 chars, 24 bytes (SBCS)" or count it by hand using the code-page linked to by bytes in the heading. – Jonathan Allan 15 hours ago
    
... as hex: 1C 1C 89 AF FE 25 32 FC 3E 53 D8 FE 24 8D 0B FE 3B F0 BD FE 26 E8 29 FB – Jonathan Allan 15 hours ago

C, 81 bytes

f(float m,float h){m/=h*h;puts(m<26?m<18.6?"Underweight":"Normal":"Overweight");}
share|improve this answer

JavaScript (ES7), 70 67 64 63 bytes

Saved 4B thanks to Arnauld

a=>b=>(a/=b*b)<25&a>=18.5?"Normal":(a<19?"Und":"Ov")+"erweight"

Usage

f=a=>b=>(a/=b*b)<25&a>=18.5?"Normal":(a<19?"Und":"Ov")+"erweight"
f(80)(1)

Output

"Overweight"

Explanation

This answer is quite trivial, though there is one clever trick: Underweight and Overweight both end in erweight, so we only have to change those characters.

I assumed that Normal means a BMI between 25 (exclusive) and 18.5 (inclusive). Underweight means a BMI less than 18.5, and Overweight means a BMI greater than or equal to 25.

share|improve this answer
    
You're right. Thanks for the 3B save. – Luke yesterday
    
You don't really need c. You could do a/=b*b. – Arnauld yesterday
    
You still need the parentheses, though. ^^ – Arnauld yesterday
    
Oops, my bad. Thanks for fixing it. – Luke yesterday
1  
@obarakon a/=b is the division assignment operator and has the same effect as a=a/b – Arnauld yesterday

Ruby, 91 77 74 67 bytes

First naive try:

->(w,h){case w/h/h
when 0..18.5
'underweight'
when 18.5..25
'normal'
else
'overweight'
end}

Second try with ”inspiration“ from previous answers:

->w,h{["#{(w/=h*h)<18.5?'und':'ov'}erweight",'normal'][(18.5..25)===(w)?1:0]}

Third try:

->w,h{["#{(w/=h*h)<18.5?'und':'ov'}erweight",'normal'][w>=18.5&&w<25?1:0]}

Fourth try:

->w,h{18.5<=(w/=h*h)&&w<25?'normal':"#{w<18.5?'und':'ov'}erweight"}

Try it online!

share|improve this answer

QBIC, 61 58 bytes

::m=a/b^2~m<18.5|?@Und`+@erweight`\~m>=25|?@Ov`+B\?@Normal

@Luke used the Force and chopped off two bytes. Thanks!

The rules change saved another byte.

Explanation:

::          gets weight and height as a and b
m=a/b^2     Calculates BMI
~m<18.5|    If BMI < 18.5 then
?@Und`      Print the string literal 'Und' (which is now A$)
+@erweight` and the string literal 'erweight'  (which is now B$)
\~m>=25|    else if the BMI is greater than or equal to 25
?@Ov`+B     Print 'Ov' and B$ ('erweight')
\?@Normal   Else, if we're here, BMI is normal.
share|improve this answer
1  
You can save 2 bytes, since Underweight and overweight both end in erweight. – Luke yesterday

Python 2, 72 bytes

lambda a,b:"UNOnovdreemrrawwlee ii gg hh tt"[(18.6<a/b/b)+(a/b/b>25)::3]

Try it online!

share|improve this answer
    
What if the BMI is 18.6 or 25. Won't it fail as it doesn't have <=? – Jack Bates yesterday
    
@JackBates Whoops. Fixed – DJMcMayhem yesterday
    
How do you get the colours for Python? I never figured it out. – Jack Bates yesterday
    
@JackBates It's just <!-- language-all: lang-python --> which TIO automatically inserted for me. – DJMcMayhem yesterday
    
Thanks for that! – Jack Bates yesterday

05AB1E, 28 bytes

n/©37;‹®25‹O’‚Š‰ß î ‚â‰ß’#è

Uses the CP-1252 encoding. Try it online!

share|improve this answer

Clojure, 63 bytes

#(condp <(/ %(* %2 %2))25"Overweight"18.5"Normal""Underweight")
share|improve this answer
    
Damn. As always, this is a shorter version of what I was thinking of. – Carcigenicate yesterday

Java 8, 61 bytes

w->h->w/h/h<18.5?"Underweight":w/h/h<25?"Normal":"Overweight"

Assign to a DoubleFunction<DoubleFunction<String>> and call thusly:

bmi.apply(50).apply(1.5)
share|improve this answer
    
You can spare one byte by reusing w: w->h->(w/=h*h)<18.5?"Underweight":w<25?"Normal":"Overweight"‌​. – Olivier Grégoire 13 hours ago
    
@OlivierGrégoire Nope :( Error: local variables referenced from a lambda expression must be final or effectively final Can't assign to w. – David Conrad 12 hours ago
1  
Oh... I checked with inlined stuff like int w = ... , h = ... ; System.out.println((w/=h*h)<18.5?"Underweight":w<25?"Normal"‌​:"Overweight"), sorry :) – Olivier Grégoire 10 hours ago
    
@OlivierGrégoire No problem. I wish Java allowed it. – David Conrad 10 hours ago

Mathematica, 67 bytes

"Normal"["Underweight","Overweight"][[Sign@⌊(2#/#2^2-37)/13⌋]]&

Uses the fact that a[b,c][[Sign@d]] returns a if d equals 0, returns b if d is positive, and returns c if d is negative. ⌊...⌋ is Mathematica's Floor function using the three-byte characters U+230A and U+230B. Couldn't figure out how to do better than using weight twice.

share|improve this answer
2  
Surprised Mathematica doesn't have a built-in for this – Daniel 22 hours ago
    
you have to go to the fan fiction for that ;) – Greg Martin 16 hours ago

PowerShell, 81 bytes

param($m,$h)('Underweight','Normal','Overweight')[(18.5,25-lt($m/($h*$h))).Count]

Try it online!

Explanation

The main bit that needs explaining is 18.5,25 -lt $b (where I'm substituting $b for the BMI which is calculated in place in the code). Most operators in PowerShell, when given an array on the left side, return an array of items that satisfy the test, instead of returning a boolean value. So this will return an empty array if $b is smaller than 18.5, an array containing only 18.5 if it's in the middle, and an array containing both 18.5 and 25 if it's larger than 25.

I use the count of the elements as an index into an array of the strings, so count 0 gets element 0 which is 'Underweight', etc.

share|improve this answer
    
@AdmBorkBork thanks! I changed the code after I wrote the explanation and before I posted it, and I had to rewrite all that stuff, basically reversing it all. Missed that one. – briantist 17 hours ago

dc, 64 bytes

[erweight][[Und]PszPq]su[[Normal]Pq]sn9k?d*/d18.5>ud25>n[Ov]PszP

Try it online!

share|improve this answer
    
Dang it! Just beat me by a few seconds. I was about to post mine, until I saw this. Anyways, here is another 64 byte answer: 3k[Overweight]??2^/dsp[[Normal]][[Underweight]]sasb25>blp18.‌​5>ap. – R. Kap yesterday
    
@R.Kap You can actually get yours to be one byte shorter than mine by omitting one of the question marks. – Mitchell Spector yesterday
    
Oh yeah, I forgot that I could do that. You can post that as your own if you want. – R. Kap yesterday
    
No, that's fine -- go ahead and post it yourself, it's your solution. (You can credit me for one byte if you want.) – Mitchell Spector yesterday
1  
All right. Thanks! :) – R. Kap yesterday

Python 3, 97 95 bytes

a,b=map(int,input().split())
a/=b*b*5
print(["UOnvd"[a>93::2]+"erweight","Normal"][93<=a<=125])

Full program.

Multiply by five to save a byte. Thanks Jonathan Allan.

Line by line:

  1. Map the two space separated user input numbers to ints. Unpack to a and b.

  2. Calculate

  3. If the bmi is between 18.6 and 25, inclusive, the expression on the right will evaluate to True. Booleans can be 0 or 1 when used as list indexes, so we get either "Normal" or the constructed string in the zero index. "erweight" is a shared suffix for the remaining two options, so it doesn't need to be repeated. Then we use the [start:stop:step] pattern of Python list/string slicing. c>18.6 will evaluate to either 0 or 1 (False or True) and becomes our start. Stop isn't indicated so we go to the end of the literal. Step is 2 so we take every second index. If start start evaluates to 1, we get "Ov", otherwise we get "Und". Either way, we append "erweight" to what we got and we have our final output.

share|improve this answer
1  
Write it as a function for 79: def f(h,w):c=h/w/w;print(["UOnvd"[c>18.6::2]+"erweight","Normal"‌​][18.6<=c<=25]) – Jonathan Allan yesterday
    
If you multiply up by five first you can save a byte by comparing to 93 and 125. If you use a lambda you have to calculate c in both places but don't need to name the function or use print(), so can do lambda h,w:["UOnvd"[h/w/w*5>93::2]+"erweight","Normal"][93<=h/w/w*5‌​<=125] for 73. – Jonathan Allan yesterday
    
...actually because you will be calculating c twice the multiply by 5 costs more than it saves in the lambda, so just lambda h,w:["UOnvd"[h/w/w>18.6::2]+"erweight","Normal"][18.6<=h/w/w‌​<=25] for 72 – Jonathan Allan yesterday
    
Jonathan Allan Somebody already did it as just a function. – mypetlion yesterday
    
That's OK, your way will out-golf them :D – Jonathan Allan yesterday

dc, 58 bytes

Fk[Ov]?2^/d[[Normal]pq][[Und]26]sasb18.5>a25>bn[erweight]p

Takes input as 2 space separated numbers in the format <mass> <height> . Outputs a string on a separate line.

Try it online!

Explanation

For the purposes of this explanation, the input is 80 1.

Fk                                                         # Set decimal precision to `16`.
  [Ov]                                                     # Push the string "Ov" onto the main stack.
                                                           # Main Stack: [[Ov]]
      ?2^/d                                                # Take and evaluate input, squaring the 2nd one, and the dividing by the first one by the 2nd. Then duplicate the result.
                                                           # Main Stack: [[Ov],80.000000000000000,80.000000000000000]
           [[Normal]pq][[Und]26]sasb                       # Push and store the executable macros "[Normal]pq" and "[Und]26" on registers "a" and "b", respectively.
                                                           # Main Stack: [[Ov],80.000000000000000,80.000000000000000], reg. a: [[[Normal]pq]], reg. b: [[[Und]26]]
                                    18.5>a25>b             # Push, "18.5" onto stack, and then pop top 2 values. If "18.5 > (top of stack)", then execute the macro on top of reg. "a", which in turn pushes the string "Und" onto the main stack followed by the number 26.
                                                           # The "26" will automatically prompt the next comparison to not execute the macro on top of reg. "b", regardless of the value on top of the main stack.
                                                           # Otherwise, if "18.5 <= (top of stack) < 25", then execute "b"s macro, which in turn pushes the string "Normal" onto the main stack, outputs it, then quits the program.
                                                           # In this case, Main stack: [[Ov]], reg. a: [[[Normal]pq]], reg. b: [[[Und]26]]
                                              n[erweight]p # If "Normal" has not been output, only then will the program get to this point. Here, it will output whatever string, either "Und" or "Ov", on top of the main stack, followed by "erweight" and a new line.
share|improve this answer

Common Lisp SBCL, 89 bytes

A function:

(defun f(w h)(set'b(/ w(* h h)))(if(< b 18.5)'underweight(if(< b 25)'normal'overweight)))

Any idea for improvement is welcomed.

share|improve this answer

Octave, 64 bytes

@(w,h){'Underweight','Normal','Overweight'}{3-sum(2*w/h^2<'%2')}

Try it online

This is an anonymous function that takes two input arguments, h (height) and w (weight).

The function creates a cell array containing there strings 'Underweight','Normal','Overweight', and outputs string number 3-sum(2*w/h^2<'%2').

Yes, that one looks a bit strange. We want the first string if w/h^2<=18.5, the second string if (w/h^2 > 18.5) & (w/h^2 < 25) and the third string if none of the above conditions are true. Instead of creating a bunch of comparisons, we could simply compare the string to: w/h^2 < [18.5, 25], which would return one of the following arrays [1 1], [0 1], [0,0] for Underweight, Normal and Overweight respectively.

[18.5,25] takes 9 bytes, which is a lot. What we do instead, is multiply the BMI by 2, and compare the result with [37, 50], or '%2' in ASCII. This saves three bytes.

share|improve this answer

Javascript (ES6), 63 bytes

(m,h)=>(w="erweight",b=m/h/h)<18.5?"Und"+w:b<25?"Normal":"Ov"+w

Example

f=(m,h)=>(w="erweight",b=m/h/h)<18.5?"Und"+w:b<25?"Normal":"Ov"+w

console.log(f(80, 1));
console.log(f(80, 2));
console.log(f(80, 3));

share|improve this answer

Perl 6, 59 bytes

{<Overweight Normal Underweight>[sum 18.5,25 X>$^a/$^b**2]}

How it works

{                                                         }  # A lambda.
                                               $^a/$^b**2    # Compute BMI from arguments.
                                     18.5,25 X>              # Compare against endpoints.
                                 sum                         # Add the two booleans together.
 <Overweight Normal Underweight>[                        ]   # Index into hard-coded list.

Too bad the string erweight has to be repeated, but all variations I tried in order to avoid that ended up increasing the overall byte count:

  • With string substitution, 62 bytes:

    {<Ov_ Normal Und_>[sum 18.5,25 X>$^a/$^b**2].&{S/_/erweight/}}
  • With string interpolation, 67 bytes:

    {$_='erweight';("Ov$_","Normal","Und$_")[sum 18.5,25 X>$^a/$^b**2]}
  • Rough translation of xnor's Python solution, 65 bytes:

    {$_=$^a/$^b**2;25>$_>=18.5??"Normal"!!<Und Ov>[$_>19]~"erweight"}
share|improve this answer

C#, 63 62 bytes

Saved 1 byte thanks to anonymous user.

w=>h=>w/h/h<18.5?"Underweight":w/h/h<25?"Normal":"Overweight";

A pretty straightforward anonymous function. The whole trick is using the ternary operator to return directly (thus omitting the return keyword, a pair of curly braces and a variable declaration and assignment).

Full program with test cases:

using System;

class BodyMassIndex
{
    static void Main()
    {
        Func<double, Func<double, string>> f =
        w=>h=>w/h/h<18.5?"Underweight":w/h/h<25?"Normal":"Overweight";

        // test cases:
        Console.WriteLine(f(80)(1));  // "Overweight"
        Console.WriteLine(f(80)(2));  // "Normal"
        Console.WriteLine(f(80)(3));  // "Underweight"
        Console.WriteLine(f(50)(1));  // "Overweight"
        Console.WriteLine(f(50)(1.5));  // "Normal"
        Console.WriteLine(f(50)(2));  // "Underweight"
    }
}
share|improve this answer
    
You can assign the value of the expression back to w and save a byte: w=>h=>(w/=h*h)<18.5?"Underweight":w<25?"Normal":"Overweight"‌​; I tried combining "underweight" and "overweight" with the common "erweight" but could never get it shorter... – TheLethalCoder 21 mins ago

Python, 75 74 bytes

lambda h,w:18.5<=w/h/h<=25and"normal"or["ov","und"][25>w/h/h]+"erwe‌​ight"

Try it online!

Fairly basic solution that takes advantage of other people's techniques for solving it.

Thanks @ovs for saving a byte.

Alternatives

1. 73 bytes

lambda h,w:"normal"if 18.5<=w/h/h<=25 else"uonvd"[25<w/h/h::2]+"erweight"

I rejected this as it was too similar to another answer I saw.

2. 71 bytes

lambda h,w:"normal"if 18.5<w/h/h<25 else"uonvd"[25<w/h/h::2]+"erweight"

I rejected this because, despite working on all the tests in the question, there are some numbers it can fail on as it's missing the = in the <=.

share|improve this answer
    
You have a bug in your function. It should be ["ov","und"][25>w/h/h] – ovs yesterday
    
lambda h,w:18.5<=w/h/h<=25and"normal"or["ov","und"][25>w/h/h]+"erwe‌​ight" is shorter – ovs yesterday
    
Thanks for noticing. Fixed. – Jack Bates yesterday
    
You don't need the space between 25 and else - but anyway, using short circuiting and/or (as @ovs commented) is shorter. – FlipTack 14 hours ago
    
@FlipTack: regarding the space between 25 and else, you definitely do need it with some (most?) interpreters (including CPython, IIRC). If you write it as 25else, the 25e is interpreted as the start of a scientific-notation numeric literal, and the interpreter then baulks when there are no following digits. – Mac 6 hours ago

Japt, 46 bytes

/=V²U<25©U¨18½?`N޵l`:ºU<19?`U˜`:"Ov")+`€³ight

Try it online!

Explanation

/=V²U<25©U¨18½?`N޵l`:ºU<19?`U˜`:"Ov")+`€³ight  
/=                               // Implicit input (U) divided by equal to
  V²                             // Second input to the power of 2
        ©                        // Shortcut for &&
          ¨                      // Shortcut for >=
                N޵l             // "Normal" compressed
               `    `            // Backticks uncompress
                      º          // Shortcut for ((
                                 // Anything wrapped in `backticks` are compressed strings
share|improve this answer

PHP, 69 68/85 bytes

If you want it to be more as a program,

$a=$args;$b=$a[0]/($a[1]*$a[1]);echo$b>=18.5&&$b<25?normal:($b<18.5?und:ov).erweight; // 85

$b=$k/($h*$h);echo$b>=18.5&&$b<25?normal:($b<18.5?und:ov).erweight; // 68
$b=$k/pow($h,2);echo$b>=18.5&&$b<25?normal:($b<18.5?und:ov).erweight; // 69

You can test with $k=80; $h=1;. I had three choices, via $args, like this or as defined values (K&H). I choose the middle as most fair.

share|improve this answer
    
This is a snippet, not a function or full program. – corvus_192 23 hours ago

Swift, 97 bytes

{(w:Float,h)->String in return 18.5<=w/h/h&&w/h/h<25 ?"normal":"\(w/h/h>25 ?"ov":"und")erweight"}
share|improve this answer

OCaml, 93 bytes

let b w h=if w/.h/.h<18.5 then"underweight"else if w/.h/.h>=25.0 then"overweight"else"normal"
share|improve this answer

R, 89 84 80 bytes

f=function(w,h){c('Overweight','Normal','Underweight')[sum(w/h^2<c(18.5,25),1)]}

With a nod to StewieGriffin's Octave answer, creates an array of strings, then sums the result of BMI < c(18.5,25) and references the array at that position + 1.

share|improve this answer

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.