| Bytes | Lang | Time | Link |
|---|---|---|---|
| nan | 241217T030234Z | user2166 | |
| 100 | PHP | 220917T161629Z | huanglx |
| 144 | Python 3 | 220915T173321Z | SectorCo |
| 084 | Excel | 220916T153641Z | Engineer |
| nan | C clang | 220915T065236Z | mousetai |
| 081 | Knight v2.0alpha | 220918T055833Z | Sampersa |
| 092 | Python 3.8 | 220917T222537Z | Albert K |
| 029 | Pyth | 220915T021303Z | hakr14 |
| 093 | Python 3 | 220916T223505Z | Zachary |
| 067 | JavaScript ES6 | 220914T230349Z | Arnauld |
| 098 | Python | 220915T203942Z | Tess2552 |
| 299 | sed E | 220915T190156Z | Jiří |
| 034 | Charcoal | 220915T195109Z | Neil |
| 090 | Retina 0.8.2 | 220915T190030Z | Neil |
| 024 | Jelly | 220915T181849Z | Jonathan |
| 124 | Red | 220915T084721Z | Galen Iv |
| 026 | 05AB1E | 220915T075336Z | Kevin Cr |
| 038 | APLDyalog Unicode | 220914T220901Z | Adá |
Here is the basic steps:
- Define a conversion scale: Map letter grades (A, B, C, etc.) to their respective grade points.
- Input the grades: Accept an array of letter grades.
- Convert to grade points: Use the defined conversion scale to get the grade points for each letter.
- Find the average: Add all the grade points together, divide by the total number of grades, and round to one decimal place.
In Javascript implementation:
function calculateGPA(grades) {
// Map of letter grades to grade points
const gradePoints = {
A: 4.0,
A_minus: 3.7,
B_plus: 3.3,
B: 3.0,
B_minus: 2.7,
C_plus: 2.3,
C: 2.0,
C_minus: 1.7,
D_plus: 1.3,
D: 1.0,
F: 0.0,
};
// Accumulate grade points
let totalPoints = 0;
grades.forEach((grade) => {
totalPoints += gradePoints[grade] || 0; // Use 0 if the grade is invalid
});
// Calculate GPA
const gpa = totalPoints / grades.length;
return Math.round(gpa * 10) / 10; // Round to 1 decimal place
}
// Example usage
const grades = ["A", "B", "B_plus", "C", "A_minus"];
console.log(calculateGPA(grades)); // Output: 3.2
PHP, 165 100 bytes
Thanks to @Steffan for the whopping 39% size reduction.
function($a){foreach($a as$v)$r+=strpos("FDCBA",$v[$l++*0])+($v[1]?$v[1]..3:0);echo round($r/$l,1);}
Original Solution
function($a){$i='array_search';$g=str_split("FDCBA");$r=0;foreach($a AS$v){@$r+=eval("return {$i($v[0], $g)}".($v[1]?$v[1].".3;":";"));}die(round($r/count($a),1));};
This is my first time golfing in PHP, so I probably missed a few places where I could have been more efficient.
Original Explanation
PHP is completely and utterly broken, which is why golfing in it is so fun. We approach this problem by using a string and splitting it, using the index as GPA value. The array_search function grabs the proper GPA value for us. We then use the + or - in the input to add or subtract 0.3 to the GPA value and use a standard algorithm to find the average value.
Python 3, 168 150 144 bytes
lambda n:int(sum([{'A':4,'A-':3.7,'B+':3.3,'B':3,'B-':2.7,'C+':2.3,'C':2,'C-':1.7,'D+':1.3,'D':1,'D-':0.7,'F':0}[i]for i in n])/len(n)*10+.5)/10
Could save some bytes on the dictionary creation.
Returns a float.
Excel, 89 84 bytes
Saved 5 bytes thanks to JvdV
=ROUND(AVERAGE(IF(A:A=0,"",FIND(LEFT(A:A),"FDCBA")-1+IFERROR(--RIGHT(A:A)&.3,0))),1)
Input is in the first column. One grade per row. When you paste the formula, Excel will automatically add a leading zero so .3 becomes 0.3.
ROUND(~,1)will round the final result to one decimal place.AVERAGE(~)will average the array we're about to calculate.IF(A:A=0,"",~)will ignore all the blank cells below the input. Without this, the above solution produces around 1 million zeroes so that throws off the average a bit.- BASE VALUE:
FIND(LEFT(A:A),"FDCBA")-1get the base GPA value of each letter from 0 - 4. - ADJUSTMENT VALUE:
IFERROR(--RIGHT(A:A)&.3,0)RIGHT(A:A)&.3combines the right-most character (which may be a letter) with.3.--~tries to coerce that value into a number which throws an error if the right-most character was a letter since something like--A.3is not a number.IFERROR(~,0)corrects all those errors to0instead.
- When you add the base value and adjustment value (which may be negative), you get the GPA value for that letter grade.
- All those grades are calculated individually and then averaged and rounded to one decimal place.
C (clang), 113 96 bytes
-17 bytes thanks to celingcat and jdt
k;i;main(j){for(;gets(&j);k++)i+=j%8<5?50-j%8*10:0,i+=(j>>=8)?132-j*3:0;printf("%.1f",i/10./k);}
Takes input as one grade per line.
Explanation
gets(&j)
Read the string into a integer field j. This is perfectly valid in C, and is a compact way to read strings of upto 3 bytes long.
j%8<5
Check for F
50-j%8*10
Extract the last byte from the string, this will be the letter (since little endian). 178-10*char will be the letter grade, 40, 30, 20, 10 etc.
0,
I think this just changed the operator precidence? Not sure.
i+=(j>>=8)?132-j*3:0
Add or subtract 3 depending on the sign, since + is 43 and - is 45
printf("%.1f",i/10./k)
Round and print the final number as a float.
Knight (v2.0-alpha), 81 bytes
O+++=s=n"";W=gP;=n+1n=s++*100&!?70=cA[g-69c*30&]g-44A]g s/=q/+/s n 5=t 10t'.'%q t
The basic algorithm is as follows, with a few golfing optimizations added:
# Set `s` and `n` to the empty string. This could be any zero value,3
# but an empty string helps with `OUTPUT`
; + = s = n ""
# Each grade is on a separate line
; WHILE = grade PROMPT
; = n + 1 n # increment the amount of grades
# The grade part is `100 * (c == 'F' ? 0 : 69 - c)`,
# ie the grade offset for normal grades, or `0` for `F`.
; = grade_part * 100 &(!? 70 = c ASCII [grade) (- 69 c) #nice
# The modifier is `grade.len == 1 ? 0 : 30 * (44 - grade[1].ascii)`
; = modifier_part * 30 (& ]grade (- 44 ASCII ]grade))
# `s` will be coerced to an int the first tiem around.
: = sum ++ grade_part modifier_part s
# Calculate the average * 10. To accommodate the rounding rule,
# we add five, then divide (`sum`, which is 100x a GPA) by ten.
; = avg / (+ /sum n 5) 10
# Now, simply output the number in the float format
: OUTPUT ++(+ "" /avg 10) '.' (% avg 10)
```
Python 3.8, 95 93 92 bytes
s=.01;i=0
for g,a,*_ in open(0):i+=1;s+='FDCBA'.find(g);exec(f's=s{a}.3')
exit(f'{s/i:.1f}')
Input (stdin)
A
B+
C
D
Very straightforward. Open stdin with open(0), then read each line with for g. Python has an annoying property where it leaves a trailing \r after each line, which we exploit in an f-string passed to exec. In case there's no appendix after grade, f-string becomes s=s\r.3, which is essentially a no-op. Very unsafe ;)
Python 3, 93 bytes
lambda l:int(0.5+sum(3*(c=='+')-3*(c=='-')or'FDCBA'.index(c)*10for c in''.join(l))/len(l))/10
Takes input as a list of strings (['A', 'B+']).
JavaScript (ES6), 67 bytes
Returns a number.
a=>(a.map(([x,y])=>t+=940.5%(n++,'0x5'+x)%85+~~(y+3),n=t=0)|t/n)/10
How?
The purpose of the black-magic formula 940.5 % ('0x5' + x) % 85 is to turn a grade into its base value pre-multiplied by \$10\$ and with an offset of \$1/2\$ for the final rounding.
| x | N = '0x5' + x | As decimal | 940.5 mod N | mod 85 |
|---|---|---|---|---|
| A | 0x5A | 90 | 40.5 | 40.5 |
| B | 0x5B | 91 | 30.5 | 30.5 |
| C | 0x5C | 92 | 20.5 | 20.5 |
| D | 0x5D | 93 | 10.5 | 10.5 |
| F | 0x5F | 95 | 85.5 | 0.5 |
We use the expression ~~(y + 3) to add an additional offset of \$\pm 3\$ if the grade is followed by + or -. This evaluates to ~~NaN (which is \$0\$) if there's no modifier.
Given the sum \$t\$ of all these values, the GPA is given by \$\lfloor t/n\rfloor/10\$, where \$n\$ is the number of grades.
JavaScript (ES6), 76 bytes
Original answer, returning a string.
a=>(eval(a.map(s=>s<'F'&&'14-0x'+s+[s[1]&&.3]).join`+`)/a.length).toFixed(1)
How?
If the grade is "F", we turn it into "false".
Otherwise:
- we add the prefix
"14-0x" - if this is a two-character grade, we add the suffix
"0.3"
Examples:
"A"is turned into"14-0xA"(\$14-10=4\$)"B+"is turned into"14-0xB+0.3"(\$14-11+0.3=3.3\$)"C-"is turned into"14-0xC-0.3"(\$14-12-0.3=1.7\$)
Python, 98 bytes
lambda i:round(sum(x*i.count(y)for y,x in zip("FDCBA-+",[*range(5),-.3,.3]))/len(i.split())+.01,1)
sed -E, 302 299 bytes
:a
s/ //
s/[A-D]/&aaaaaaaaaa/g
y/ABCD/BCDF/
s/\+/aaa/
/-/{s/-//;s/a//;s/a//;s/a//}
s/Fa/aF/
ta
s/F/!F/
:b
s/F/a/
s/(a+)(a*!)\1$/X\2\1/
tb
s/!.*/\U&/
s/AA?/a/g
/(a+)!\1$/s/a/X/
s/[a!]//g
s/X{10}/Y/g
s/$/I.0/
:c
/X/y/012345678/123456789/
/Y/y/IJKLMNOPQ/JKLMNOPQ9/
s/X//
s/Y//
tc
y/IJKLMNOPQ/012345678/
Explanation:
# store dividend as unary "a" and divisor as unary "F"
:a
s/ // # removes all spaces
s/[A-D]/&aaaaaaaaaa/g # for each of letters ABCD ten "a" are added
y/ABCD/BCDF/ # each of letters ABCD are substracted to BCDE
s/\+/aaa/ # for every + three "a" are added
/-/{s/-//;s/a//;s/a//;s/a//} # for every - three "a" are removed
s/Fa/aF/ # sort the string into format "aaaaaFFFFF"
ta
# now perform the division
s/F/!F/ # transforms string to format "aaaaa!FFFFF"
:b
s/F/a/ # transforms string to format "aaaaa!aaaaa"
s/(a+)(a*!)\1$/X\2\1/ # if there are more or equal "a" on left side than on the right append "X" to beggining
tb
# now do the rounding
s/!.*/\U&/ # transorm "a" after "!" to "A"
s/AA?/a/g # now the amount of "A" is divided by 2
/(a+)!\1$/s/a/X/ # check if the reminder is more or equal to half of divisor and if so, then add one more "X"
s/[a!]//g # remove leftover characters
# now the result is stored as unary "X"
# conversion from unary to decimal
s/X{10}/Y/g # convert every ten "X" to "Y"
s/$/I.0/ # adds string for output "I.0"
:c
/X/y/012345678/123456789/ # for every "X" the "0" is increased
/Y/y/IJKLMNOPQ/JKLMNOPQ9/ # for every "Y" the "I" is increased
s/X//
s/Y//
tc
y/IJKLMNOPQ/012345678/ # now the "I" is transformed to digit
Charcoal, 34 bytes
WS⊞υ⁺⌕FDCBA§ι⁰×·³⁻№ι+№ι-﹪%.1f∕ΣυLυ
Try it online! Link is to verbose version of code. Takes input as a list of newline-terminated grade strings. Explanation:
WS
Repeat for each input grade...
⊞υ⁺⌕FDCBA§ι⁰×·³⁻№ι+№ι-
... calculate its value by looking up the letter in the string FDCBA and adjusting by 0.3 depending on the number of +s or -s.
﹪%.1f∕ΣυLυ
Take the average and format it to one decimal place.
Retina 0.8.2, 90 bytes
T`L`4-0
\d
0$&$*
1
20$*
\+
6$*
1{6}-
O`.
0+
$.&$*1$.&$*1,$.&$*
\D*(1+),(\1)*1*
$#2
.$
.$&
Try it online! Link includes test cases. Output always includes the decimal but omits the leading 0 on scores of less than 1. Explanation:
T`L`4-0
Translate uppercase letters to digits A=4, B=4, C=2, D=1, otherwise 0.
\d
0$&$*
Convert to unary and prefix a marker 0 to keep count of the number of grades.
1
20$*
Multiply by 20.
\+
6$*
Add 6 for a +.
1{6}-
Subtract 6 for a -.
O`.
Collect the markers and scores together.
0+
$.&$*1$.&$*1,$.&$*
Add the count to the sum (so that the division will round) and double the count.
\D*(1+),(\1)*1*
$#2
Divide the sum by the doubled count.
.$
.$&
Divide by 10.
Jelly, 24 bytes
ON%7’»0_2¦€4ḋ10,3Æm+.Ḟ÷⁵
A monadic Link that accepts a list of lists of characters and yields a number.
How?
ON%7’»0_2¦€4ḋ10,3Æm+.Ḟ÷⁵ - Link: list of grades e.g. ['A-', 'B+', 'F', 'F']
O - ordinals (vectorises) [[65, 45], [66, 43], [70], [70]]
N - negate [[-65, -45], [-66, -43], [-70], [-70]]
%7 - mod seven [[5, 4], [4, 6], [0], [0]]
’ - decrement [[4, 3], [3, 5], [-1], [-1]]
»0 - max with zero [[4, 3], [3, 5], [0], [0]]
2¦€ - from 2nd of each:
_ 4 - subtract four [[4, -1], [3, 1], [0], [0]]
ḋ10,3 - dot product with [10,3] [37, 33, 0, 0]
Æm - mean 17.5
+. - add a half 18.0
Ḟ - floor 18
÷⁵ - divide by ten 1.8
If banker's rounding was used \$21\$ bytes is possible with ON%7’»0_2¦€4Uḅ.3Æmær1 (U reverses each, ḅ.3 converts from base \$0.3\$, and ær1 uses Python's round to round to 1 decimal place).
Red, 124 bytes
func[a][forall a[parse a/1[change p: skip(rejoin[max 69 - p/1 0" "])opt[skip insert" .3"]]a/1: do a/1]round/to average a .1]
More readable:
f: func [a][
forall a [
parse a/1 [
change set g skip (rejoin [max 69 - g 0 space])
opt [skip insert " 0.3"]
]
a/1: do a/1
]
round/to average a 0.1
]
The input is a block (list) of strings. I modify each string in place by changing the letter with the corresponding grade value and optionally appending "0.3" after + or -. Then the string is replaced by its evaluated value. Finally I find and round the average of the block.
05AB1E, 30 26 bytes
ε14sáC-.3y¦'\ìθ.VDd*}ÅA1.ò
-4 bytes by semi-porting @Arnauld's JavaScript answer
Try it online or verify all test cases.
Original 30 bytes answer:
•H₁yΔи•.¥RA4£…+ -â¦ðмuIkèÅAòT/
Try it online or verify all test cases.
Explanation:
ε # Map over the (implicit) input-list:
14 # Push 14
s # Swap so the current grade-string is at the top
á # Only keep its letters (removing a potential "+"/"-")
C # Convert it to 'binary': "A"=10, "B"=11, "C"=12, "D"=13, "F"=15
- # Subtract it from the 14
.3 # Push 0.3
y # Push the current grade-string again
¦ # Remove its first character (the letter)
'\ì '# Prepend a leading "\"
θ # Pop and only keep its last character
.V # Execute it as 05AB1E code
# ("+"/"-" does what you'd expect; "\" discards the 0.3 from the stack)
D # Duplicate the decimal
d # Check if it's non-negative (1 if >=0; 0 if <0)
* # Multiply that to the decimal to change the "F"=1 to 0
} # Close the map
ÅA # Get the average of this list
1.ò # Round it to 1 decimal point
# (after which the result is output implicitly)
•H₁yΔи• # Push compressed integer 73343343343
.¥ # Undelta its digits with leading 0: [0,7,10,13,17,20,23,27,30,33,37,40]
R # Reverse it: [40,37,33,30,27,23,20,17,13,10,7,0]
A # Push the lowercase alphabet
4£ # Only keep its first four characters: "abcd"
…+ - # Push string "+ -"
â # Get the cartesian product of the two: ["a+","a ","a-",...,"d+","d ","d-"]
¦ # Remove the leading "a+"
ðм # Remove all spaces from each string
Il # Push the input-list, and convert each grade-string to lowercase
k # Get the indices of each lowercase grade-string in the string-list we've created)
# (-1 for "f", since it's not in the list)
è # Use that to index into the earlier list of integers
# (-1 wraps around to the last item, which is the 0)
ÅA # Get the average of this list
ò # Round it to an integer
T/ # Divide it by 10
# (after which the result is output implicitly)
See this 05AB1E tip of mine (section How to compress large integers?) to understand why •H₁yΔи• is 73343343343.
APL(Dyalog Unicode), 38 bytes SBCS
Anonymous prefix lambda taking a string argument of whatever format is convenient.
{1⍕(+/l,.3×-⌿'+-'∘.=⍵)÷≢l←0⌈5-⎕A⍳⍵∩⎕A}
{…} "dfn"; argument is ⍵:
⍵∩⎕A intersection of argument and uppercase Alphabet
⎕A⍳ indices of those letters in the uppercase Alphabet (A=1)
5- subtract those indices from 5
0⌈ maximum of 0 and those numbers
l← assign to l
≢ count those
(…)÷ divide the following by that:
'+-'∘.=⍵ comparison table of signs vs the argument characters
-⌿ subtract the bottom row from the top row
.3× multiply those differences by 0.3
l, prepend l
+/ sum
1⍕ format as string with 1 decimal
