Reputation: 501
Question in book:
Write a program that translates a letter grade into a number grade. Letter grades are A
, B
, C
, D
, and F
, possibly followed by +
or –
. Their numeric values are 4
, 3
, 2
, 1
, and 0
. There is no F+
or F–
. A +
increases the numeric value by 0.3
, a –
decreases it by 0.3
. However, an A+
has value 4.0
.
Enter a letter grade: B-
The numeric value is 2.7
.
Use a class Grade with a method getNumericGrade
.
So my question is : I know my code is right, I can run it. But I was curious whether there is a shortcut or a better version of this code that makes it look more professional and easier to read. Any suggestions or examples would greatly be appreciated.
Code:
import java.util.Scanner;
public class grade
{
private double numericValue = 0;
private String grade = "";
public grade()
{
Scanner in = new Scanner(System. in );
System.out.print("Enter Grade: ");
grade = in .nextLine();
}
public double getNumericGrade()
{
if (grade.equals("A+") || grade.equals("A"))
{
numericValue = 4.0;
}
else if (grade.equals("A-"))
{
numericValue = 3.7;
}
else if (grade.equals("B+"))
{
numericValue = 3.3;
}
else if (grade.equals("B"))
{
numericValue = 3.0;
}
else if (grade.equals("B-"))
{
numericValue = 2.7;
}
else if (grade.equals("C+"))
{
numericValue = 2.3;
}
else if (grade.equals("C"))
{
numericValue = 2.0;
}
else if (grade.equals("C-"))
{
numericValue = 1.7;
}
else if (grade.equals("D+"))
{
numericValue = 1.3;
}
else if (grade.equals("D"))
{
numericValue = 1.0;
}
else if (grade.equals("F"))
{
numericValue = 0;
}
else
{
System.out.println("Letter not in grading system");
}
return numericValue;
}
}
Upvotes: 4
Views: 7692
Reputation: 1
I'd deal with the '+' or '-' separately.
Check for the letter, then add or subtract the grade value appropriately if it contains a '+' or '-'.
EDIT: Personally, I prefer switch-case to long if-elseif statements, but either one will work well enough.
Upvotes: 0
Reputation: 29436
You can define the mappings and rules separately:
public static enum Grade {
// Letter grades are A, B, C, D, and F
// Their numeric values are 4, 3, 2, 1, and 0
A(4),B(3),C(2),D(1),F(0);
public final double score;
private Grade(double d) {
this.score = d;
}
// Grades are possibly followed by + or –
// There is no F+ or F–
// a + increases the numeric value by 0.3, a – decreases it by 0.3
// However, an A+ has value 4.0
public double getModifiedScore(char sign) {
switch (sign) {
case '+':
return score + (score < 4 && score > 0 ? 0.3 : 0);
case '-':
return score + (score > 0 ? -0.3 : 0);
default:
throw new IllegalArgumentException("Invalid sign");
}
}
}
Then just use them (example assumes you have validated the input):
public static double getNumericGrade(String s){
Grade g = Grade.valueOf(s.substring(0, 1));
if(s.length() > 1){
return g.getModifiedScore(s.charAt(1));
}else {
return g.score;
}
}
Upvotes: 6
Reputation: 129517
Use a Map
that maps characters (in this case A
..D
,F
) to the corresponding values (4
..0
).
When you process your input:
+
or -
at the end, lookup the first character and deal with the second appropriately (according to the rules you posted, remembering to handle the special cases such as A+
). You can try something like this:
private static final Map<Character, Double> grades = new HashMap<>();
static {
grades.put('A', 4.0);
grades.put('B', 3.0);
grades.put('C', 2.0);
grades.put('D', 1.0);
grades.put('F', 0.0);
}
public static double getNumericGrade(String input) {
if (input == null || input.isEmpty() || input.length() > 2
|| input.matches("F[+-]")
|| !grades.containsKey(input.charAt(0))) // validate input
throw new IllegalArgumentException();
double val = grades.get(input.charAt(0));
if (input.length() == 1) {
return val;
} else if (input.charAt(1) == '+') {
return (input.charAt(0) == 'A') ? val : val + 0.3;
} else if (input.charAt(1) == '-') {
return val - 0.3;
} else {
throw new IllegalArgumentException();
}
}
Upvotes: 1
Reputation: 106450
One approach is to use switch
- which works with String
in Java 7.
// Yes, I'm passing grade and shadowing numericValue, since those properties
// aren't used anywhere else in the class.
public double getNumericGrade(String grade) {
double numericValue = 0;
switch (grade) {
case "A+":
case "A":
numericValue = 4.0;
break;
case "A-":
numericValue = 3.7;
break;
case "B+":
numericValue = 3.3;
break;
case "B":
numericValue = 3.0;
break;
case "B-":
numericValue = 2.7;
break;
case "C+":
numericValue = 2.3;
break;
case "C":
numericValue = 2.0;
break;
case "C-":
numericValue = 1.7;
break;
case "D+":
numericValue = 1.3;
break;
case "D":
numericValue = 1.0;
break;
case "F":
numericValue = 0;
break;
default:
System.out.println("Letter not in grading system");
break;
}
return numericValue;
}
...but that reads pretty verbosely.
Another alternative approach is to take what rules we have about numeric grades, and write something a little more terse.
Here's a solution, which makes use of those rules. It may read a little more verbose and might not be all that different than the switch, but it's another way to write it.
public double getNumericGradeRefactored(String grade) {
Map<Character, Double> gradeMap = new HashMap<Character, Double>(){{
put('A', 4.0);
put('B', 3.0);
put('C', 2.0);
put('D', 1.0);
put('F', 0.0);
}};
// split result
char[] gradeParts = grade.toCharArray();
double result = gradeMap.get(gradeParts[0]);
if(gradeParts.length > 1) {
switch(gradeParts[1]) {
case '+':
result += 0.3;
break;
case '-':
result -= 0.3;
break;
}
}
return result;
}
Upvotes: 1
Reputation: 31952
In addition to @Thilos suggestion as to how to store the actual data, I would like to add that in such cases, you don't want to store both, in this case, String
and int
version of the data, unless performance is EXTREMELY critical.
While this doesnt look like a big deal now, in many cases, you run the risk of loosing sync between them, i.e String
says one thing and the int
says another, and you run into bugs due to this.
And your constructor shouldnt prompt for input, unless that becomes very common.. it is cleaner to take a String
.
Upvotes: 0
Reputation: 262554
I would use a lookup table:
private final static Map<String,Double> gradeLookup =
new HashMap<String, Double>();
gradeLookup.put("A-", 3.7);
numericValue = gradeLookup.get(grade);
Another good option is a switch statement, which as of Java 7 finally works with Strings.
Both options give you built-in input validation, so that the user cannot enter things like 'G' or 'F+' or 'foo'.
Upvotes: 5