DEV Community

dev.to staff
dev.to staff

Posted on

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!

Latest comments (20)

Collapse
 
devparkk profile image
Dev Prakash
Collapse
 
lprakashv profile image
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"
    }
}
Collapse
 
alexanderholman profile image
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

Collapse
 
dak425 profile image
Donald Feury

I concur with this response.

Collapse
 
alexanderholman profile image
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.`
Collapse
 
willsmart profile image
willsmart • Edited

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.)

Collapse
 
craigmc08 profile image
Craig McIlwrath

Have you tested if your code is faster than a simpler conditional version (as most other solutions are written)?

Collapse
 
willsmart profile image
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.

Collapse
 
raradek profile image
Kedar Hnalvy

LabVIEW: ;)

Collapse
 
jamesdengel profile image
James dengel

Awesome, haven't seen LabVIEW since I was at uni.

Collapse
 
stoutlabs profile image
Daniel Stout • Edited

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"; 
}
Collapse
 
lordapple profile image
LordApple • Edited

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";
    };
Collapse
 
dak425 profile image
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)
    }
}

Collapse
 
craigmc08 profile image
Craig McIlwrath

Haskell

bmi :: Double -> Double -> String
bmi weight height
  | x <= 18.5 = "Underweight"
  | x <= 25 = "Normal" 
  | x <= 30 = "Overweight"
  | otherwise = "Obese"
  where x = weight / (height*height)
Collapse
 
brightone profile image
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"
    }
}