## DEV Community is a community of 891,295 amazing developers

We're a place where coders share, stay up-to-date and grow their careers.

# Daily Challenge #57 - BMI Calculator

Write a function that calculates body mass index
Note: bmi = weight / height ^ 2

if bmi <= 18.5 return "Underweight"
if bmi <= 25.0 return "Normal"
if bmi <= 30.0 return "Overweight"
if bmi > 30 return "Obese"

This challenge comes from wichu on CodeWars. Thank you to CodeWars, who has licensed redistribution of this challenge under the 2-Clause BSD License!

Want to propose a challenge idea for a future post? Email yo+challenge@dev.to with your suggestions!

## Discussion (22) Well, I tried something different:

``````f=(w,h,t=[18.5,25,30],k=['Underweight','Normal','Overweight'])=>k[t.indexOf(t.find(v=>w/(h^2)<=v))]||'Obese'
``````

Apart from being ugly, it works like this:

• Store the BMI steps in "t": `t=[18.5,25,30]`
• Store the associated labels in "k": `k=['Underweight','Normal','Overweight']`
• Find the first BMI step which is greater than the provided BMI: `t.find(v=>w/(h^2)<=v)`. For example, it will return `25` if the processed BMI is between 18.5 and 25.
• Find the associated array index: `t.indexOf(...)`, in order to get the same element in the label array: `k[...]`. For example, if `25` is returned, it will fetch `k`, hence `"Normal"`
• Finally, if no label is found (hence, if the BMI is greater than 30), return `"Obese"` Rémi Lavedrine

That is an interested method, but come back at your code in a few months with absolutely no explanation and I do think that it is not going to be that easy.

I think that a code should be self explanatory (especially to newcomers on your codebase) and, even if this one is a tour-de-force in terms of one-liner, it is not self explanatory. 😉

That is why, I would find that a switch case method would be better. I completely agree with you on this one! (Hi fellow french dev!) I tend to try to golf a lot in these challenges, even though I completely agree on the fact that it's as ugly as it can be. That way, I can discover new principles (like the `reduce` method, that I hated a few months back even if it is really useful) that I can later use on my projects or at work.

Indeed, this challenge was clearly made for a switch/case solution, but as I had some free time I though I could try something different haha Florin Pop

I like it! ``````bmi :: Double -> Double -> String
bmi weight height
| x <= 18.5 = "Underweight"
| x <= 25 = "Normal"
| x <= 30 = "Overweight"
| otherwise = "Obese"
where x = weight / (height*height)
`````` Alexander Holman

Might propose a change? Instead of underweight, overweight, obsese, etc, how about we replace the return values with "no idea, consult a health professional and have your body composition correctly measured"? It will be significantly more accurate! :D Alexander Holman

Something like:

``````(w, h) => `A BMI of \${w/h**2}, means nothing. For an accurate representation of body mass measure water displacement in a big bath tub! To determine how healthy you are, consult a healthcare professional, do see how trip you are have your body composition measured, and to see how fit you are visit any good gym and undertake a fitness test in the area you wish to evaluate.`
`````` LordApple • Edited on

This seems to do the job

C++:

``````    const auto& bmi = [](const float& weight, const float& height){
const float bmi = weight / (height * height);
return bmi <= 18.0f ? "Underweight" :
bmi <= 25.0f ? "Normal" :
bmi <= 30.0f ? "Overweight" :
"Obese";
};
`````` Michael Kohl • Edited on

F#:

``````module BMI

[<Measure>]
type kg

[<Measure>]
type m

[<Measure>]
type lb

[<Measure>]
type inch

let (|LessOrEqual|_|) (target : float) (value : float) =
if value <= target then Some()
else None

let bmi ratio =
match ratio with
| LessOrEqual 18.5 -> "Underweight"
| LessOrEqual 25.0 -> "Normal"
| LessOrEqual 30.0 -> "Overweight"
| _ -> "Obese"

module Metric =
let calculate (weight : float<kg>) (height : float<m>) : string =
float (weight / (height * height)) |> bmi

module Imperial =
let calculate (weight : float<lb>) (height : float<inch>) : string =
float (weight / (height * height)) * 703. |> bmi
``````

When reading the problem description I wasn't 100% sure which units the weight and height were supposed to be in. I googled and found both a metric and imperial definition, so I decided to use units of measure and nested modules so callers of the code need to be explicit. Example:

``````BMI.Metric.calculate 85.0<kg> 1.85<m>;;
val it : string = "Normal"

BMI.Imperial.calculate 187.39<lb> 72.83<inch>;;
val it : string = "Normal"

BMI.Imperial.calculate 85<kg> 72.83<inch>;;
Stopped due to error
System.Exception: Operation could not be completed due to earlier error
Type mismatch. Expecting a
'float<lb>'
but given a
'float<kg>'
The unit of measure 'lb' does not match the unit of measure 'kg' at 0,23
``````

The active pattern `LessOrEqual` was purely for fun, one could just use `if-else` expressions or pattern matching with guards. Daniel Stout • Edited on

I couldn't resist posting a quick JavaScript solution... 😀

``````const calcBMI = (height, weight) => {
const bmi = Number(weight / (height * height)).toFixed(1);
return bmi <= 18.5 ? "underweight" :
bmi <= 25 ? "normal" :
bmi <= 30 ? "overweight" : "obese";
}
`````` willsmart • Edited on

Let's say you need to run this a few billion times times, then a more complex look up table approach might be worth it.

``````function indexFromBmi(bmi) {
// turn the BMI into an ranged integer still giving with enough precision and reach for our labels
return Math.max(0, Math.ceil((Math.min(30.5, bmi) - 18.5) / 0.5));
}
const bmi_lut = []
bmi_lut[indexFromBmi(18.5)] = 'Underweight';
bmi_lut[indexFromBmi(25)] = 'Normal';
bmi_lut[indexFromBmi(30)] = 'Overweight';
bmi_lut[indexFromBmi(30.5)] = 'Obese';

// fill in the entries below each label, so that, for example, the entry for bmi 27 will be "Overweight"
for (let index = bmi_lut.length - 1, label; index >= 0; index--) {
if (bmi_lut[index]) label = bmi_lut[index];
else bmi_lut[index] = label;
}

function bmiLabel(weight, height) {
return bmi_lut[indexFromBmi(weight / height ** 2)];
}
``````
``````> bmiLabel(-100000000,1)
< "Underweight"
> bmiLabel(18.5,1)
< "Underweight"
> bmiLabel(18.5001,1)
< "Normal"
> bmiLabel(25,1)
< "Normal"
> bmiLabel(25.0001,1)
< "Overweight"
> bmiLabel(30,1)
< "Overweight"
> bmiLabel(30.001,1)
< "Obese"
> bmiLabel(10000000000,1)
< "Obese"
// Test the weight/height relationship
> bmiLabel(25 * 1e20 ** 2, 1e20)
< "Normal"
// Try some heavy lifting
> for (let i=1e9; i; i--) bmiLabel(18.5,1);
< //took 23 seconds for a billion bmis in the js console just now (oldish mac pro)
``````

(in JS because I like it, and I find it can be nice to mock up performance code in a slowish language first up.) willsmart

Fair point Craig, in this case it turns out to be a bunch slower due to the complexity in `indexFromBmi`.
I just figured it was worth posting as an alternative approach as there are problems where luts are gold. In this case apparently not.

I'd say that here it's best to just use

``````function bmiLabel(weight, height) {
const bmi = weight / height ** 2;
return bmi <= 18.5 ? 'Underweight' : bmi <= 25 ? 'Normal' : bmi <= 30 ? 'Overweight' : 'Obese';
}
``````

and be done with it. Oleksii Filonenko

Rust:

``````fn bmi(weight: f64, height: f64) -> &'static str {
let bmi: f64 = weight / height.powi(2);

if bmi <= 18.5 {
"Underweight"
} else if bmi <= 25.0 {
"Normal"
} else if bmi <= 30.0 {
"Overweight"
} else {
"Obese"
}
}
`````` Jérémie Astor • Edited on

A solution in gwion

``````fun string bmi(float weight, float height) {
height *=> height;
weight / height => float bmi;
if(bmi <= 18.5)
return "Underweight";
if(bmi <= 25.0)
return "Normal";
if(bmi <= 30.0)
return "Overweight";
return "Obese";
}
`````` Lalit Vatsal

Scala:

``````object Body {
def calculateBMI(weight: Double, height: Double): String = (weight / (height * height)) match {
case x if x <= 18.5 => "Underweight"
case x if x <= 25 => "Normal"
case x if x <= 30 => "Overweight"
case _ => "Obsese"
}
}
`````` Donald Feury

I did two functions, one that expects imperial units (Go America!) and one that expects metric units (the rest of the world!).

body.go

``````package body

const (
underweight string = "Underweight"
normal      string = "Normal"
overweight  string = "Overweight"
obese       string = "Obese"
)

// Attributes represents some properties of a person's physique, such as weight and height
type Attributes struct {
Height float64
Weight float64
}

// BMI gives a description of your body health based on your attributes using body mass index
func BMI(attr Attributes) string {
return status((attr.Weight / (attr.Height * attr.Height)) * 703)
}

// BMIMetric serves the same purpose as BMI but expects the attributes to be in the metric scale (kilograms, meters)
func BMIMetric(attr Attributes) string {
return status(attr.Weight / (attr.Height * attr.Height))
}

func status(bmi float64) string {
switch {
case bmi <= 18.5:
return underweight
case bmi <= 25.0:
return normal
case bmi <= 30.0:
return overweight
default:
return obese
}
}

``````

body_test.go

``````package body

import "testing"

type testCase struct {
description string
input       Attributes
expected    string
}

func TestBMI(t *testing.T) {
testCases := []testCase{
{
"underweight boi",
Attributes{71, 116.0},
underweight,
},
{
"normal boi",
Attributes{71, 147.0},
normal,
},
{
"overweight boi",
Attributes{67, 190},
overweight,
},
{
"obese boi",
Attributes{65, 210},
obese,
},
}

for _, test := range testCases {
if result := BMI(test.input); result != test.expected {
t.Fatalf("FAIL: %s - BMI(%+v): %s - expected %s", test.description, test.input, result, test.expected)
}
t.Logf("PASS: %s", test.description)
}
}

func TestBMIMetric(t *testing.T) {
testCases := []testCase{
{
"underweight boi",
Attributes{1.8034, 52.61671},
underweight,
},
{
"normal boi",
Attributes{1.8034, 66.67808},
normal,
},
{
"overweight boi",
Attributes{1.7018, 86.18255},
overweight,
},
{
"obese boi",
Attributes{1.651, 95.2544},
obese,
},
}

for _, test := range testCases {
if result := BMIMetric(test.input); result != test.expected {
t.Fatalf("FAIL: %s - BMIMetric(%+v): %s - expected %s", test.description, test.input, result, test.expected)
}
t.Logf("PASS: %s", test.description)
}
}

``````