Hello I'm making a quiz app and I want to compare user answers to the correct answers. I'm planning to do that with two string lists as I collect the answers in strings and I also have the correct answers in strings.
The problem that I'm facing is that half of the questions are True/False questions so the answers are in "True" or "False" strings, the other half are questions where a calculation has to be performed, therefore the answer is a number/double which I store as a string.
I want to give a range of answer acceptance of 0.1 so if the correct answer is 22.5 an answer of 22.6 would still be considered correct. This, however, makes it impossible to compare the value of the "number" strings with the Equals() method.
So I'm ideally looking for an if statement where I want to say:
if the element is convertible to a double convert it and check if its value is the same as the correct answer or within the acceptance range
else, check if the element is equal to the correct answer
For now I have made a console app to try and solve this problem where I have defined the two lists which look like this:
static void Main(string[] args)
{
List<string> givenAnswers = new List<string>()
{
"True","False","True","False","True",
"60","50","2.321","0.8","1.55"
};
List<string> correctAnswers = new List<string>()
{
"True","False","False","True","False",
"70","20","1.231","0.5","1.25"
};
correctAnswers.ForEach(Console.WriteLine);
Console.ReadLine();
}
The main problem is that c# doesnt return false if the element is not convertible but gives error which breaks the program.
(I was previously making the comparison on my frontend - java script where an unsuccessful conversion would return false)
CodePudding user response:
Let's extract a method to compare two answers:
private static bool IsCorrectAnswer(string answer,
string expectedAnswer,
double tolerance = 0.1) {
// Let's not be peevish and tolerate leading / trimming spaces,
// ignore cases i.e. let " TRUE " be equal to "True"
if (string.Equals(answer?.Trim(),
expectedAnswer?.Trim(),
StringComparison.OrdinalIgnoreCase))
return true;
// If we have two double values, let's to parse them and compare
// with provided tolerance
if (double.TryParse(answer, out var leftValue) &&
double.TryParse(expectedAnswer, out var rightValue))
return Math.Abs(leftValue - rightValue) <= tolerance;
//TODO: you may want to add support for other types (say, dates) here
return false;
}
CodePudding user response:
I didn't get your string list of booleans and numbers, but the general check would look like this:
foreach (var answer in givenAnswers)
{
if (double.TryParse(answer, NumberStyles.Any, CultureInfo.InvariantCulture, out double answerVal) && answerVal == 0.8)
{
Console.WriteLine("Nailed it!");
}
}
I had to use "invariant culture" because in my country, 0.8 is written as "0,8" and thus "0.8" is parses to 8...
I wouldn't go into lengths about the "nearby" numbers, because it's very complicated. Comparing Floating Point Numbers, 2012 Edition
CodePudding user response:
var convertible = int.TryParse("60", out_)
CodePudding user response:
You can use double.TryParse to check if a string
is parsable to a double
and parse it at the same time when it is.
bool IsAcceptableAnswser(string givenAnswer, string correctAnswer)
{
if (double.TryParse(correctAnswer, out var correctNumber) && double.TryParse(givenAnswer, out var givenNumber))
{
// When the answers are doubles, use use the double values.
var diff = Math.Abs(correctNumber - givenNumber);
return diff <= 0.1;
}
else
{
// When the answers aren't doubles, use string.Equals.
return string.Equals(givenAnswer, correctAnswer);
}
Notes
- I used
string.Equals
for simplicity, but you could of course use something else to be more permissive around casing. - You might also want to check the other overloads of
double.TryParse
if culture is an issue. - I used
0.1
directly for readability. It would be preferable to use a constant or receive it as parameter.