### Daily Challenge #68 - Grade Book

#### dev.to staff on September 16, 2019

Today's challenge is to write a function that accepts three integer values, calculates the mean, then returns the letter value associated with that... [Read Full] 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:

Here's mine:

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

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})`;

}
``````

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

"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) "-" "+")
(filter (fn [[_ [low high]]]
(and (>= mean low) (< mean high))))
(map key)
first)]
(str letter sign)))
``````

Elm

``````module GradeBook exposing (gradeBook)

case compare (modBy 10 mean) 5 of
EQ ->
""

LT ->
"-"

GT ->
"+"

gradeBook : Int -> Int -> Int -> String
let
mean =

in
if mean < 60 then

else if mean < 70 then

else if mean < 80 then

else if mean < 90 then

else
``````

Tests

``````module GradeBookTest exposing (suite)

import Expect
import Test exposing (Test)

suite : Test
suite =
[ 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
]
``````

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

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

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

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'

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)

``````

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)

if mean >= 90 && mean <= 100 {
} else if mean >= 80 && mean < 90 {
} else if mean >= 70 && mean < 80 {
} else if mean >= 60 && mean < 70 {
} else if mean >= 0 && mean < 60 {
}

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

}

func main() {

fmt.Println(output)
}
``````

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 };

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';
``````

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

### 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 _          => ""
}
``````

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)

``````

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"
``````

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
``````

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'
}
``````

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

(toNumber average)
in
if average < 60 then

else if average < 70 then  