HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpMajor

Converting the weight of a potato into a letter grade

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
theintogradeweightpotatoletterconverting

Problem

I've just started learning C# using Rob Miles' C# Programming Yellow Book and some related lab exercises from his website. I did one of them and produced a solution that works. In Miles' book, he says that nesting if and else brackets is clumsy and introduces the switch. But switch apparently doesn't work with Boolean operators, e.g. case x < 201, so I can't use it. I could just have independent, non-nested if statements, though their conditions would have to be longer. Any other ideas?

```
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

class Program
{
static int GetIntNumber(int min, int max, string prompt, string inputPrompt)
{
int number;

Console.Write(prompt);
do //loops as long as input is not between min and max
{
while (!int.TryParse(Console.ReadLine(), out number)) //loops as long as the input is not an integer
{
Console.Write("\nYou must enter an integer number!: ");
}
if (number max) Console.Write(inputPrompt); // max);

return number;
}

static void printGrade(string grade)
{
Console.WriteLine("\nYour potato is grade " + grade);
}

static void Main()
{

do
{
int weight = GetIntNumber(0, 1500, "\nEnter the weight of the potato in grams: ", "\nEnter an integer between 0 and 1500: ");

if (weight < 201)
{
printGrade("X");
}
else
{
if (weight < 401)
{
printGrade("A");
}
else
{
if (weight < 801)
{
printGrade("B");
}
else
printGrade("Z");
}
}
Console.WriteLine("\nPress any key to c

Solution

You are very much correct that switch statements cannot take boolean operators like that: switch statements are used to build jump tables, and can only use constants to do so. However, you can easily come up with a fairly robust solution to your problem using a Dictionary and some LINQ.

Since you tagged your question beginner I'll be gentle with the explanation.

The first thing we'll do is make a Dictionary which represents the lowest value associated with each grade.

var gradeMap = new Dictionary
{
    [0] = "X",
    [201] = "A",
    [401] = "B",
    [801] = "Z",
};


So, this means if the value is >= 801, then it's Z, and so on.

Next, we'll simply one-line this entire operation in LINQ:

gradeMap.OrderByDescending(x => x.Key).FirstOrDefault(x => x.Key <= testWeight).Value


What's happening here? LINQ is Language Integrated Query, if you've ever worked with SQL you might recognize the Query bit, essentially, LINQ allows you to build code which looks and act's like a query against an IEnumerable object (which Dictionary is).

So if we take this step-by-step, we can start with our dictionary as follows:

0: X
201: A
401: B
801: Z


The gradeMap.OrderByDescending(x => x.Key) bit will first take the entire gradeMap dictionary, and order it by the key value in highest-to-lowest order:

801: Z
401: B
201: A
0: X


Next, we do a FirstOrDefault(x => x.Key <= testWeight), which will loop through all the elements (in the backwards order we have them) that are in the dictionary, and find the first one that matches our expression (x.Key <= testWeight). The x.Key is the reference to the key of the dictionary (our int values):

801: Z (Skipped)
401: B (Skipped)
201: A (Matched)


So, now we have the 201: A item from the dictionary, the last part is to get .Value on it which returns the string portion of it:

A


And viola, we have our value. :)

We can combine this together and get:

var testWeight = 205;
var grade = gradeMap.OrderByDescending(x => x.Key).FirstOrDefault(x => x.Key <= testWeight).Value;


So, the final thing we have to do is combine it with your original programme:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

class Program
{
    static int GetIntNumber(int min, int max, string prompt, string inputPrompt)
    {
        int number;

        Console.Write(prompt);
        do  //loops as long as input is not between min and max
        {
            while (!int.TryParse(Console.ReadLine(), out number)) //loops as long as the input is not an integer
            {
                Console.Write("\nYou must enter an integer number!: ");
            }
            if (number  max) Console.Write(inputPrompt); // max);

        return number;
    }

    static void printGrade(string grade)
    {
        Console.WriteLine("\nYour potato is grade " + grade);
    }

    static void Main()
    {
        do
        {
            var gradeMap = new Dictionary
            {
                [0] = "X",
                [201] = "A",
                [401] = "B",
                [801] = "Z",
            };

            int weight = GetIntNumber(0, 1500, "\nEnter the weight of the potato in grams: ", "\nEnter an integer between 0 and 1500: ");

            printGrade(gradeMap.OrderByDescending(x => x.Key).FirstOrDefault(x => x.Key <= weight).Value);

            Console.WriteLine("\nPress any key to check the grade of another potato.");
        } while (Console.ReadKey(true).Key != ConsoleKey.Escape );

        Console.Read();
    }
}


The next best thing would be to extract the LINQ logic into a new method:

public string GetGrade(int weight)
{
    var gradeMap = new Dictionary
    {
        [0] = "X",
        [201] = "A",
        [401] = "B",
        [801] = "Z",
    };

    return gradeMap.OrderByDescending(x => x.Key).FirstOrDefault(x => x.Key <= weight).Value;
}


Then our Main method becomes:

static void Main()
{
    do
    {
        int weight = GetIntNumber(0, 1500, "\nEnter the weight of the potato in grams: ", "\nEnter an integer between 0 and 1500: ");

        printGrade(GetGrade(weight));

        Console.WriteLine("\nPress any key to check the grade of another potato.");
    } while (Console.ReadKey(true).Key != ConsoleKey.Escape );

    Console.Read();
}


Now if you need to add a new grade, for whatever reason, it's trivial.

Also: if you're not using C#6.0, you will have to replace var gradeMap with the following:

var gradeMap = new Dictionary
{
    {0, "X"},
    {201, "A"},
    {401, "B"},
    {801, "Z"}
};


Good work so far, hopefully you continue improving and learn more regarding C# and the paradigms it holds. :)

The answer by t3chb0t is also very good, I recommend reading it thoroughly as well (especially the bit about method overloading).

Code Snippets

var gradeMap = new Dictionary<int, string>
{
    [0] = "X",
    [201] = "A",
    [401] = "B",
    [801] = "Z",
};
gradeMap.OrderByDescending(x => x.Key).FirstOrDefault(x => x.Key <= testWeight).Value
0: X
201: A
401: B
801: Z
801: Z
401: B
201: A
0: X
801: Z (Skipped)
401: B (Skipped)
201: A (Matched)

Context

StackExchange Code Review Q#146307, answer score: 26

Revisions (0)

No revisions yet.