DEV Community

dev.to staff
dev.to staff

Posted on

Daily Challenge #68 - Grade Book

Today's challenge is to write a function that accepts three integer values, calculates the mean, then returns the letter value associated with that grade. If the mean of those three integers ends in a number greater than five, append a plus sign to the letter grade. If it is less than five, append a minus sign.

Numerical Score      Letter Grade
90 <= score <= 100    'A'
80 <= score < 90      'B'
70 <= score < 80      'C'
60 <= score < 70      'D'
 0 <= score < 60      'F'

Examples:
grade(64, 55, 92) => C- (70.3)
grade(99, 89, 93) => A- (93.6)
grade(33, 99, 95) => C+ (75.6)

Happy coding!


This challenge was inspired by CodeWars user danleavitt0. 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!

Top comments (20)

Collapse
 
alvaromontoro profile image
Alvaro Montoro

CSS

This will only work on Safari because, for some forsaken reason, Safari is the only browser that supports min() and max() at the moment. Which is funny considering that normally it is the other way around 🤷‍♂️

This can be represented as a decision machine with different ranges for different grades. Then the idea would be to have an element to which we pass the three grades as CSS variables (all integers or it will fail), calculate the z-index to set which element should be visible. I would need to double check the logic, I think it's correct. Also, I think the code can be simplified.

Here is the code running. Remember it will only work on Safari:

Collapse
 
cgty_ky profile image
Cagatay Kaya

Too much ternary operators...

const grade = (a,b,c) =>{
    const mean = (a + b + c) / 3;

    const prefix = mean % 10 < 5 ? '-': 
                   mean % 10 > 5 ? '+': '';

    return   mean < 59 ?          `F (${mean})`:
             mean < 70 ? `D${prefix} (${mean})` : 
             mean < 80 ? `C${prefix} (${mean})`: 
             mean < 90 ? `B${prefix} (${mean})` : 
                         `A${prefix} (${mean})`;

}
Collapse
 
aminnairi profile image
Amin • Edited

You could use something like that:

({"-1": "-", "1": "+", "0": ""})[mean.toString().localeCompare("5")]

Not tested but I believe it would work in JavaScript.

Collapse
 
peledzohar profile image
Zohar Peled

I've Started to write a c# answer only to find out I'm going the exact same route as your answer...

Collapse
 
kerrishotts profile image
Kerri Shotts

Here's mine:

const sum = (...nums) => nums.reduce((total, cur) => total + cur, 0);
const meanGrade = (...grades) => sum(...grades) / grades.length;
const letterForGrade = grade => {
    const lastDigit = grade % 10;
    const gradeLetter = Object.entries({90: "A", 80: "B", 70: "C", 60: "D", 0: "F"})
        .sort(([a], [b]) => a < b)
        .find(([minGrade]) => grade >= minGrade)
        [1] + ((lastDigit < 5) ? "-" :
               (lastDigit > 5) ? "+" : "");
    return gradeLetter;
};

Gist: gist.github.com/kerrishotts/559154...

Collapse
 
casiimin profile image
Casi Imin

Nice Js!

Collapse
 
brightone profile image
Oleksii Filonenko

Rust:

fn grade(a: u32, b: u32, c: u32) -> String {
    let mean = (a + b + c) / 3;
    let letter = match dbg!(mean) {
        _ if (90..=100).contains(&mean) => "A",
        _ if (80..90).contains(&mean) => "B",
        _ if (70..80).contains(&mean) => "C",
        _ if (60..70).contains(&mean) => "D",
        _ if (0..60).contains(&mean) => "F",
        _ => unreachable!("mean ({}) can't be < 0", mean),
    };
    let sign = if mean % 10 < 5 { "-" } else { "+" };
    format!("{}{}", letter, sign)
}
Collapse
 
casiimin profile image
Casi Imin • Edited

Python

#The function
def call_mean (x,y,z):
  mean = ((x+y+z)/3)
  mean_s = (str(mean))
  print ("\nMean: ",mean)
  if (mean > 100): print("\nWrong input, try again...")
  else:
    #The statement
    if ((mean <= 100 and mean >= 90)):
      if (mean_s[1] >= '5'): print ("A+")
      else: print ("A-")
    elif ((mean < 90 and mean >= 80)):
      if (mean_s[1] >= '5'): print ("B+")
      else: print ("B-")
    elif ((mean < 80 and mean >= 70)):
      if (mean_s[1] >= '5'): print ("C+")
      else: print ("C-")
    elif ((mean < 70 and mean >= 60)):
      if (mean_s[1] >= '5'): print ("D+")
      else: print ("D-")
    elif ((mean < 60 and mean >= 0)):
      if (mean_s[1] >= '5'): print ("F+")
      else: print ("F-")

#Three value from input
x = int(input("first int: "))
y = int(input("second int: "))
z = int(input("third int: "))

#Call the function
call_mean(x,y,z)
Collapse
 
brightone profile image
Oleksii Filonenko

Clojure (my new adventure):

(def grades {"A" [90 101]
             "B" [80 90]
             "C" [70 80]
             "D" [60 70]
             "F" [0 60]})

(defn grade
  "Calculates a letter for the mean of given grades."
  [a b c]
  (let [mean (quot (+ a b c) 3)
        sign (if (< (rem mean 10) 5) "-" "+")
        letter (->> grades
                   (filter (fn [[_ [low high]]]
                             (and (>= mean low) (< mean high))))
                   (map key)
                   first)]
    (str letter sign)))
Collapse
 
aminnairi profile image
Amin • Edited

Elm

module GradeBook exposing (gradeBook)


computeGradeSign : Int -> String
computeGradeSign mean =
    case compare (modBy 10 mean) 5 of
        EQ ->
            ""

        LT ->
            "-"

        GT ->
            "+"


gradeBook : Int -> Int -> Int -> String
gradeBook grade1 grade2 grade3 =
    let
        mean =
            toFloat (grade1 + grade2 + grade3) / 3

        gradeSign =
            computeGradeSign <| round mean
    in
    if mean < 60 then
        "F" ++ gradeSign

    else if mean < 70 then
        "D" ++ gradeSign

    else if mean < 80 then
        "C" ++ gradeSign

    else if mean < 90 then
        "B" ++ gradeSign

    else
        "A" ++ gradeSign

Tests

module GradeBookTest exposing (suite)

import Expect
import GradeBook exposing (gradeBook)
import Test exposing (Test)


suite : Test
suite =
    Test.describe "Grade Book"
        [ Test.test "It should return C- when passing 64, 55 & 92" <|
            \_ ->
                Expect.equal "C-" <| gradeBook 64 55 92
        , Test.test "It should return A- when passing 99, 89 & 93" <|
            \_ ->
                Expect.equal "A-" <| gradeBook 99 89 93
        , Test.test "It should return C+ when passing 33, 99 & 95" <|
            \_ ->
                Expect.equal "C+" <| gradeBook 33 99 95
        , Test.test "It should return C when passing 64, 70 & 92" <|
            \_ ->
                Expect.equal "C" <| gradeBook 64 70 92
        ]
Collapse
 
rebeccaskinner profile image
Rebecca Skinner

Here's my type-level implementation in haskell:

{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE DataKinds #-}

module Lib where
import GHC.TypeLits

data PlusMinus g = Plus g | Minus g | None g

data Grade = A | B | C | D | F

infixr 5 :->
data GradeMapping minScore grade = minScore :-> grade

type StandardGradingCurve = '[90 :-> A, 80 :-> B, 70 :-> C, 60 :-> D, 0 :-> F]

type family LookupGrade (c :: [GradeMapping score grade]) (s :: score) :: grade where
  LookupGrade m k = LookupGrade' m k k

type family LookupGrade' (c :: [GradeMapping score grade]) (s :: score) (s' :: score) :: grade where
  LookupGrade' '[] k k' = F
  LookupGrade' ((k :-> v) ': c) k k' = v
  LookupGrade' ((k :-> v) ': c) 0 k' = LookupGrade' c k' k'
  LookupGrade' ((k :-> v) ': c) n k' = LookupGrade' ((k :-> v) ': c) (n - 1) k'
  LookupGrade' ((kvm) ': c) k k' = LookupGrade' c k k'

type family IfThenElse (cond :: Bool) (whenTrue :: b) (whenFalse :: b) :: b where
  IfThenElse True trueBranch falseBranch = trueBranch
  IfThenElse False trueBranch falseBranch = falseBranch

type family LessThan (a :: Nat) (b :: Nat) :: Bool where
  LessThan a a = False
  LessThan a 0 = False
  LessThan 0 b = True
  LessThan a b = LessThan (a - 1) (b - 1)

type family OnesFamily (n :: Nat) :: Nat where
  OnesFamily 0 = 0
  OnesFamily n = IfThenElse (LessThan n 10) n (OnesFamily (n - 10))

type family MakePlusMinus (n :: Nat) (val :: a) :: PlusMinus a where
  MakePlusMinus score val =
    IfThenElse (LessThan (OnesFamily score) 5) (Minus val) (IfThenElse (LessThan 5 (OnesFamily score)) (Plus val) (None val))

type family Sum (vals :: [Nat]) :: Nat where
  Sum vals = Sum' 0 vals

type family Sum' (total :: Nat) (vals :: [Nat]) :: Nat where
  Sum' n '[] = n
  Sum' n (a ': as) = Sum' (n + a) as

type family Length (vals :: [a]) :: Nat where
  Length '[] = 0
  Length (val ': vals) = 1 + (Length vals)

type family Mean (vals :: [Nat]) :: Nat where
  Mean vals = Div (Sum vals) (Length vals)

type family CalcGrade (vals :: [Nat]) :: PlusMinus Grade where
  CalcGrade vals = MakePlusMinus (Mean vals) (LookupGrade StandardGradingCurve (Mean vals))
Collapse
 
eddiehale3 profile image
Eddie Hale • Edited

My first stab at writing something other than "Hello World!" in golang:

package main

import (
    "fmt"
    "math"
)

func grade(x, y, z float64) string {
    mean := float64((x + y + z) / 3)
    grade := ""

    if mean >= 90 && mean <= 100 {
        grade = "A"
    } else if mean >= 80 && mean < 90 {
        grade = "B"
    } else if mean >= 70 && mean < 80 {
        grade = "C"
    } else if mean >= 60 && mean < 70 {
        grade = "D"
    } else if mean >= 0 && mean < 60 {
        grade = "F"
    }

    if math.Mod(mean, 10.0) < 5 && grade != "F" {
        grade += "-"
    } else if math.Mod(mean, 10.0) >= 5 && grade != "F" {
        grade += "+"
    }

    return grade
}

func main() {
    //output := grade(64, 55, 92)
    //output := grade(99, 89, 93)
    output := grade(33, 99, 95)

    fmt.Println(output)
}
Collapse
 
teaglebuilt profile image
dillan teagle • Edited

This is my quick python solution

def grade(x, y, z):
    board = { 9: "A", 8: "B", 7: "C", 6: "D", 5: "F" }
    sum = x + y + z
    mean = sum / 3
    if mean >= 5:
        return board.get(mean)

Collapse
 
mbaas2 profile image
Michael Baas • Edited

APL (I'm using Dyalog APL):

{md←10|m←(+⌿÷⍴)⍵ ⋄ ((1+60 70 80 90 101⍸m)⊃'FDCBA'),' -+'[1+(md≠5)×1+(,5)⍸md]}

If you think that's too short to work - here's the proof:
First, let's define that as a function:
getMark←{md←10|m←(+⌿÷⍴)⍵ ⋄ ((1+60 70 80 90 101⍸m)⊃'FDCBA'),' -+'[1+(md≠5)×1+(,5)⍸md]}

And now you can simply do
getMark 64 55 92
C-
getMark 99 89 93
A-
getMark 33 99 95
C+

And if you still don't believe me, try it here or there! ;-)
(These online solutions use the symbol ≢ instead of ⍴ - they are equivalent in this case, but ≢ renders incorrectly on dev.to. There's also an issue with another APL-Character: ⍸ - if someone from the dev-team would contact me, I'd love to help sorting that out...)

Collapse
 
kvharish profile image
K.V.Harish • Edited

My solution in js

const grade = (...values) => {
  const grades = {9: 'A', 8: 'B', 7: 'C', 6: 'D'},
    mean = values.reduce((acc, value) => acc += value, 0) / values.length;
  return grades[Math.floor(mean / 10)] ? `${grades[Math.floor(mean / 10)]}${mean >= (Math.floor(mean / 10) * 10 + 5) ? '+' : '-'}` : Math.floor(mean / 10) > 9 ? 'A+' : 'F'
}
Collapse
 
choroba profile image
E. Choroba

Perl solution, using a regex to extract the last digit (but we need to replace 100 by 99 for it to work).

#!/usr/bin/perl
use warnings;
use strict;
use List::Util qw{ sum };

sub grade {
    my $mean = int(sum(@_) / @_);
    return 'F' if $mean < 60;

    $mean = 99 if $mean == 100;
    my $sign = ($mean =~ /(.)$/)[0] < 5 ? '-' : '+';

    return qw( D C B A )[ ($mean - 60) / 10 ] . $sign
}

use Test::More tests => 6;

is grade(100, 100, 100) => 'A+';
is grade( 60,  60,  60) => 'D-';
is grade( 64,  55,  92) => 'C-';
is grade( 99,  89,  93) => 'A-';
is grade( 33,  99,  95) => 'C+';
is grade( 60,  60,  59) => 'F';
Collapse
 
colorfusion profile image
Melvin Yeo

I wrote this in Python with the assumption that the input of the function will all be integers between 0 and 100.

def grade(*grades):
    grade_range = [(90, 'A'),(80, 'B'),(70, 'C'),(60, 'D')]
    mean = 0

    for _, score in enumerate(grades):
        mean += score

    mean = int(mean / len(grades))

    polarity = "+" if mean % 10 >= 5 else "-"

    for grade in grade_range:
        if mean >= grade[0]:
            return grade[1] + polarity

    return "F" + polarity
Collapse
 
avalander profile image
Avalander

Scala

def grade (a: Int, b: Int, c: Int): String = {
  val average = (a + b + c) / 3f
  letter(average) ++ sign(average)
}

def letter (grade: Float): String =
  grade match {
    case x if x >= 90 => "A"
    case x if x >= 80 => "B"
    case x if x >= 70 => "C"
    case x if x >= 60 => "D"
    case _            => "F"
  }

def sign (grade: Float): String =
  (grade % 10) match {
    case x if x < 5 => "-"
    case x if x > 5 => "+"
    case _          => ""
  }