# 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!

### Discussion 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:

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}); }  You could use something like that: ({"-1": "-", "1": "+", "0": ""})[mean.toString().localeCompare("5")]  Not tested but I believe it would work in JavaScript. I've Started to write a c# answer only to find out I'm going the exact same route as your answer... 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)  + ((lastDigit < 5) ? "-" : (lastDigit > 5) ? "+" : ""); return gradeLetter; };  Nice Js! 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) }  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 >= '5'): print ("A+") else: print ("A-") elif ((mean < 90 and mean >= 80)): if (mean_s >= '5'): print ("B+") else: print ("B-") elif ((mean < 80 and mean >= 70)): if (mean_s >= '5'): print ("C+") else: print ("C-") elif ((mean < 70 and mean >= 60)): if (mean_s >= '5'): print ("D+") else: print ("D-") elif ((mean < 60 and mean >= 0)): if (mean_s >= '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)  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)))  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 ]  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))  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) }  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)  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...) 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' }  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 =~ /(.)$/) < 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';


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

mean += score

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

return "F" + polarity


F#:

module DailyChallenge

if mean < 60. then 'F'
else if mean < 70. then 'D'
else if mean < 80. then 'C'
else if mean < 90. then 'B'
else 'A'

match int mean % 10 with
| x when x > 5 -> "+"
| 5 -> ""
| _ -> "-"



Tests:

module DailyChallengeTest

open FsUnit.Xunit
open Xunit
open DailyChallenge

[<Fact>]
let C-() = grade 64 55 92 |> should equal "C-"

[<Fact>]
let A-() = grade 99 89 93 |> should equal "A-"

[<Fact>]
let C+() = grade 33 99 98 |> should equal "C+"

[<Fact>]
let C() = grade 64 70 92 |> should equal "C"

[<Fact>]
let F() = grade 55 55 55 |> should equal "F"


### 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 =
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 =
case x if x < 5 => "-"
case x if x > 5 => "+"
case _          => ""
}


Solved Using Purescript inspired from Amin Nairi

grade :: Int -> String
case compare (mod 10 average) 5 of
EQ ->
""

LT ->
"-"

GT ->
"+"

resultGrade :: Int -> Int -> Int -> String
let

finalGrade = grade $fromMaybe 0$ fromNumber \$ Math.round
(toNumber average)
in
if average < 60 then

else if average < 70 then  