views:

276

answers:

5

How can I make my following code "DRY" (Dont Repeat Yourself)

- (void)updateLetterScore { // NOT DRY... Must fix
    if (percentScore < 60.0)
     letterLabel.text = [NSString stringWithFormat:@"F"];

    if (percentScore > 59.0 && percentScore < 64.0)
     letterLabel.text = [NSString stringWithFormat:@"D-"];

    if (percentScore > 64.0 &&  percentScore < 67.0)
     letterLabel.text = [NSString stringWithFormat:@"D"]; 

    if (percentScore > 66.0 &&  percentScore < 70.0)
     letterLabel.text = [NSString stringWithFormat:@"D+"]; 

    if (percentScore > 69.0 &&  percentScore < 74.0)
     letterLabel.text = [NSString stringWithFormat:@"C-"]; 

    if (percentScore > 73.0 &&  percentScore < 76.0)
     letterLabel.text = [NSString stringWithFormat:@"C"];

    if (percentScore > 76.0 &&  percentScore < 80.0)
     letterLabel.text = [NSString stringWithFormat:@"C+"];

    if (percentScore > 79.0 &&  percentScore < 84.0)
     letterLabel.text = [NSString stringWithFormat:@"B-"];

    if (percentScore > 83.0 &&  percentScore < 86.0)
     letterLabel.text = [NSString stringWithFormat:@"B"];

    if (percentScore > 85.0 &&  percentScore < 90.0)
     letterLabel.text = [NSString stringWithFormat:@"B+"];

    if (percentScore > 89.0 &&  percentScore < 94.0)
     letterLabel.text = [NSString stringWithFormat:@"A-"];

    if (percentScore > 93.0 &&  percentScore < 100.0)
     letterLabel.text = [NSString stringWithFormat:@"A"];

    if (percentScore == 100)
     letterLabel.text = [NSString stringWithFormat:@"A+"];
}

Thanks for the tips. I just wanna know what you guys think because this little snippit looks HORRENDOUS in my code.

+9  A: 

One way (pseudo-code since I don't know objective C):

grades = ["F", "D-", "D", ...]
scores = [60.0, 64.0, 67.0, ...]

for(i = 0; i < grades.count; i = i + 1)
{
   if(score < scores[i])
   {
     letterLabel.text = [NSString stringWithFormat:@"%@", grades[i]]
     break;
   }
}
Talljoe
Instead of two arrays you could use a map.
Deinumite
I don't know objective C, either--but my inclination in such a situation is to put the data into records and have only one array of records instead of two of values--no way to accidentally get them out of sync in editing.
Loren Pechtel
The problem with a map is that you need a way to move through them in order. I agree about the records. The thought crossed my mind but I went with simplicity.
Talljoe
well the problem with this is tehre is no range for each grade. And its not always the perfect number, such as 60, 64, or 67. SO whats the solution for that?
Daniel Kindler
This expression [NSString stringWithFormat:grades[i]]should be written as [NSString stringWithFormat:@"%@", grades[i]]If a grade was ever to chagne to be named "%@", the previous code would crash. It's true that is very unlikely that you'd ever do such a good thing, it's good to be in the habbit of never passing a raw string as a format string.Also, why not just make scores an array of objective-c strings? Then you don't need to allocate one when the method is invoked, you just get to use one of the compiled in constant strings.
Jon Hess
@Daniel, There is a range for each grade. If the grade is less than 60 then it's an F. Otherwise if it's less than 64 (and by definition of the previous check, greater-than-or-equal-to 60) then it's a D-. Otherwise, if it's less than 67 (and, by definition... >= 64) it's a D. Etc.
Talljoe
+2  A: 

Talljoe showed one way to do it, but the idea is simply store all the scores in some sort of lookup table

hhafez
+1  A: 

If this abomination was in my care, I would extract all the magic values and place them in a table and iterate through the table checking to see which range your percentScore falls in. You might want to recheck all your ranges, they don't seem to address all the values that percentScore could assume.

Erik
+3  A: 

Like others, I would put the values into a table, and then scan the table. The table is small, it probably isn't worth making some more O() efficent structure like a tree.

typedef struct {
    float minPercent;
    NSString *letterGrade;
} GradeRange

- (NSString *)letterGradeForPercentage:(float)percentage {
    GradeRange ranges[] = {{.minPercent = 100, .letterGrade = @"A+"},
                           ...
                           {.minPercent = 66.0, .letterGrade = @"D+"},
                           {.minPercent = 64.0, .letterGrade = @"D"}};

    NSString *grade = nil;
    for(NSInteger i = 0; !grade && i < (sizeof(ranges) / sizeof(ranges[0])); i += 1) {
        if (percentage >= ranges[i].minPercent) {
            grade = ranges[i].letterGrade;
        }
    }
    return grade;
}
Jon Hess
A: 

With all of one hundred entries involved here, use an array containing the grade letter code and [array objectAtIndex: i] offset into the array based on the integer grade value. Done.

With Cocoa, you can either build this array directly in the code, or load the array from backing store (arrayWithContentsOfFile:) and allow Cocoa to sort out the storage.

You can one-plus this particular design by permitting the user to adjust the grade ranges by rewriting the saved grade array, and the code itself doesn't change.

Stephen Hoffman