How to output data in a certain form? - c#

I have a homework problem that I need help with.
This is the assignment question
So I have the following code for my class:
public Tests(String firstName, String lastName, int[] testScores)
{
this.firstName = firstName;
this.lastName = lastName;
this.testScores = testScores;
}
//First Name getter and setter
public String GetFirstName()
{
return firstName;
}
public void SetFirstName(String firstName)
{
this.firstName = firstName;
}
//Last Name getter and setter
public String GetLastName()
{
return lastName;
}
public void SetLastName(String lastName)
{
this.lastName = lastName;
}
//Test Scores getter and setter
public int[] GetTestScores()
{
return testScores;
}
public void SetTestScores(int[] testScores)
{
this.testScores = testScores;
}
//This method calculates the average of the test scores for each student.
public double CalculateAverage(int[] testScores)
{
int sum = 0;
for (int i = 0; i < testScores.Length; i++)
{
sum = sum + testScores[i];
}
double average = sum / testScores.Length;
return average;
}
//This method returns the letter grade from the test average.
public char WhatIsGrade(double avgTestScore)
{
char grade;
if (avgTestScore >= 90)
{
grade = 'A';
return grade;
}
else if (avgTestScore >= 80 && avgTestScore < 90)
{
grade = 'B';
return grade;
}
else if (avgTestScore >= 70 && avgTestScore < 80)
{
grade = 'C';
return grade;
}
else if (avgTestScore >= 60 && avgTestScore < 70)
{
grade = 'D';
return grade;
}
else
{
grade = 'F';
return grade;
}
}
//Updates the score of a test
public void modifyGrade(int testNumber, int newScore)
{
testScores[testNumber] = newScore;
}
So once I did the class, I thought it would be pretty simple with writing the driver code in the main method.
Here is what I have so far in my main method:
static void Main(string[] args)
{
int[] scores = new int[5];
Console.WriteLine("Student 1 First Name: ");
String student1First = Console.ReadLine();
Console.WriteLine("Student 1 Last Name: ");
String student1Last = Console.ReadLine();
for (int i = 0; i < scores.Length; i++)
{
Console.WriteLine("Enter score: ");
scores[i] = Convert.ToInt32(Console.ReadLine());
}
The only thing I can think of is to write this code for each student. But then I run into the problem of finding the sum for each student's scores. I also run into the problem of how to store each student's data so that I can print it out at the end in a table form. I am guessing that I have to import the class so I did so like this:
Tests t1 = new Tests(firstName, lastName, arrayName)
But I get confused as to how it will be for each one and how to actually store anything in these. Can somebody explain step by step? Also if I have done anything wrong in my class, can somebody point it out?

Alright lets start with your class. So looks like you're missing 2 things in your Tests constructor, you're gonna wanna add the logic to set the value of the average and the letter grade to the constructor. Gonna want to make a getter and setter for each of those as well. In the CalculateAverage() and WhatIsGrade() you dont need the arguments since you've already pass that data to the class, so you can get rid of those and change the logic to reference the data you set in your constructor. So the constructor should look something like this now:
public Tests(String firstName, String lastName, int[] testScores)
{
this.firstName = firstName;
this.lastName = lastName;
this.testScores = testScores;
average = CalculateAverage();
grade = WhatIsGrade();
}
Now onto the main class, unless your teacher wants all the data entered manually you can just hardcode all the data. If not you can just take all the user input in a loop. And according to your assignment you need to store each Tests object into an array of objects. So hardcoded it would look something like this
Tests[] testArray = new Tests[]
{
new Tests(your data here),
new Tests(your data here),
etc
};
For output you'll want to make a method that overrides the default ToString method, you can read more about that here: https://learn.microsoft.com/en-us/dotnet/api/system.object.tostring?view=netframework-4.8
That'll be where you do the formatting your teacher requested, then its just as simple as running through that array of objects with a loop and calling the ToString method on each object in the array.
Alright so outside of the loop go ahead and make an empty array of objects. Then within the loop you can do what you've already done in your main, just within the loop. You'll want to store each of the user inputs into a temporary variable just like you've already got in your main then at the very end of the loop you'll just need to add each object to your array of objects. Loop should look something like this:
for(int i = 0; i < testArray.Length; i++)
{
int[] scores = new int[5];
Console.WriteLine("Student First Name: ");
String studentFirst = Console.ReadLine();
Console.WriteLine("Student Last Name: ");
String studentLast = Console.ReadLine();
for (int j = 0; j < scores.Length; j++)
{
Console.WriteLine("Enter score: ");
scores[j] = Convert.ToInt32(Console.ReadLine());
}
testArray[i] = new Tests(studentFirst, studentLast, scores);
}
So in your overridden ToString method, before the return statement you'll want to declare a temporary string variable to hold the scores the iterate through the scores array in a for loop and add each element to the string. It'll look something like this:
string scores = "";
for(int i = 0; i < testScores.Length; i++)
{
scores = scores + testScores[i] + " ";
}
return GetFirstName() + " " + GetLastName() + " " + scores + GetAverage() + " " + GetGrade();

Related

Find the index of an object in an array with unknown objects

In a program that asks the name, surname and age of 10 student, at the end we have to enter the name of one of them and the program has to give their place in the array. It always tells me that the place is 0. Here is the code:
public class Program_pinakes
{
public static void Main(string[] args)
{
int i;
string[] a = new string[10];
string[] b = new string[10];
int[] c = new int[10];
int index = 0;
for (i = 0; i < 10; i++)
{
Console.Write("Name: ");
a[i] = Console.ReadLine();
Console.Write("Surname: ");
b[i] = Console.ReadLine();
Console.Write("Age: ");
c[i] = Int32.Parse(Console.ReadLine());
}
Console.Write("Name of student you are looking for: ");
string name = Console.ReadLine();
if (name == a[i])
index = i;
Console.Write("Student"+a[i]+" is located at"+index+" place.");
}
}
Edit: Thank you all for your answers. I found out about the IndexOutOfRange problem and solved it easily. The main problem was about the index returning 0. Now, after I put it on the loop to search the array again, it returns 0 for the first name, 1 for the second and so on. Is this the right way, or it should be returning 1 for the first name, 2 for second etc?
At the end of the loop i has the value of 10. (This is the reason why the cancel condition: i < 10 becomes true and the loop exits).
So when you try to access a[i] after the loop it will throw an IndexOutOfRange exception. Because you have not enough elements in your array.
Solution: To find an element you need to loop again through the array and compare each value with the search-name:
for(int i = 0; i < 10; i++ )
{
if (name == a[i])
{
index = i;
break; // finish the loop, you have found the first occurence
}
}
Console.Write("The first occurence of Student: "+name+" is located at"+index+" place.");
Edit: There can be a case of course that the search-name is not in the list. You can handle such a case by initializing the index value with -1 and check for it after the search loop:
int index = -1;
// search loop here
if (index > -1)
{
Console.Write("The first occurence of Student: " + name + " is located at" + index + " place.");
}
else
{
Console.Write("Student: "+ name +" could not be found!");
}
This code you have posted is not working, as it will throw an exception System.IndexOutOfRangeException.
However apart from this exeption, my answer would be:
becuase,
if (name == a[i])
index = i;
Console.Write("Student"+a[i]+" is located at"+index+" place.");
by doing this you are checking if ith entry of the array is the one you desired or not, if yes then you are setting index with i.
But what about it doesn't match? index will stay with the value as it was initialized with. Here it is zero what you are getting in output.
you should be doing like below.
index = -1;
for(int j = 0; i < a.Length; j++ )
{
if (name == a[j])
{
index = j;
break;
}
}
if(index != -1)
Console.Write("Student" + name + " is located at" + (index + 1) + " place.");
else
Console.Write("Student" + name + " is not in list");
Consider creating a Student object which holds all your student specific data:
class Student
{
public string Name { get; set; }
public string Surename { get; set; }
public int Age { get; set; }
}
Now create a list of Student:
var students = new List<Student>();
And in your loop create a new Student object for every repetition:
for (i = 0; i < 10; i++)
{
Console.Write("Name: ");
var name = Console.ReadLine();
Console.Write("Surname: ");
var surname = Console.ReadLine();
Console.Write("Age: ");
var age = Int32.Parse(Console.ReadLine());
students.Add(new Student {Name = name, Surname = surname, Age = age});
}
After the loop you ask for the name of the student you want the index for and use LINQ to find him in the list:
Console.Write("Name of student you are looking for: ");
string name = Console.ReadLine();
var foundStudent = students.FirstOrDefault(s => s.Name.Equals(name));
if(foundStudent != null)
{
var index = students.IndexOf(foundStudent);
Console.Write($"Student {foundStudent.Name} is located at {index} place.");
}
else
{
Console.WriteLine($"No student for name {name} found");
}
NOTE:
Check out C#'s string interpolation feature for building strings with variables. I find it way more cleaner.
The index always is 0 because your if (name == a[i]) statement is not in a lopp. So you do not actually search in the array but only check if it is like the 11th (array starts to count at 0) element. (i is set to 10 after your for loop)

Method being run through twice when it should only run once

class Myclass
{
public string Driver1()
{
string a = "";
Console.Write("Please enter drivers name: ");
a = Console.ReadLine();
return a;
}
public int numberOfTrips()
{
int a = 0;
{
Console.Write("Enter the number of trips: ");
a = Convert.ToInt32(Console.ReadLine());
}
return a;
}
public List<float> Payments()
{
List<float> a = new List<float>();
float input;
for (int i = 0; i<numberOfTrips(); i++)
{
Console.Write("Enter payment {0}: ", (1 + i));
input = float.Parse(Console.ReadLine());
Console.WriteLine("Payment added");
a.Add(input);
}
return a;
}
}
class Program
{
static void Main(string[] args)
{
Myclass a = new Myclass();
string name = a.Driver1();
int trip = a.numberOfTrips();
float total = a.Payments().Sum();
Console.WriteLine("\nDriver: {0}\n" + "Number of trips: {1}\n" + "Total payment: {2}\n", name, trip, total);
}
}
The issue i am having is that the "public int numberOfTrips()" method is running twice before it gets to the method containing the for loop. I think this is to do with the fact i am using it within the for loop to specify when the loop should stop. I am guessing i have done this wrong so how would i correct this? I need the user to be able to set the how many times it will ask for payment.
Any help is appreciated.
Just pass the number from numberOfTrips as a parameter to Payments:
public List<float> Payments(int tripCount)
{
List<float> a = new List<float>();
float input;
for (int i = 0; i < tripCount; i++)
{
Console.Write("Enter payment {0}: ", (1 + i));
input = float.Parse(Console.ReadLine());
Console.WriteLine("Payment added");
a.Add(input);
}
return a;
}
In your Main method:
Myclass a = new Myclass();
string name = a.Driver1();
int trip = a.numberOfTrips();
float total = a.Payments(trip).Sum();
Instead of calling numberOfTrips() in Main() as well as in Payments() you might try creating an instance variable or static variable in MyClass. Then you can fetch the number of trips from that variable after all payments are calculated.
That is correct. The first time it runs is in Main to set the 'trip' variable. The second time it runs is in Payments, inside the for loop declaration.

Two instances of a class: only sort 1

I have a basic class that has four attributes (Patient). In Main() I have reserved the memory for the array and before I actually create the instance, I ask the user for the account number to ensure it doesn't already exist within the array. So BinarySearch requires it to be sorted but as soon as I sort it the ability to do the for loop is lost.
//Variables
int intMaxNum = 5; //set max number of patients to 5
int intInputValue;
int intResult;
string strTempName;
int intTempAge;
double dblTempTotal;
Patient[] objectPatient = new Patient[intMaxNum]; //create an array of references
for (int x = 0; x < objectPatient.Length; ++x)
{
//attempt to create a 'shadow' class to search through and keep integrity of main class (objectPatient)
Patient[] tempobjectPatient = new Patient[intMaxNum];
tempobjectPatient = objectPatient;
if (x > 0)
{
Console.Write("\n***Next Patient***");
Array.Sort(tempobjectPatient); //this will sort both objects even though I send the temporary class only - interface impact I'm sure
}
//ask for the Patient Account number
Console.Write("\nEnter Patient Account Number: ");
ReadTheAccountNumber:
intInputValue = Convert.ToInt32(Console.ReadLine());
//create temporary class for comparison
Patient SeekPatient = new Patient();
SeekPatient.PatientNumber=intInputValue; // reset the default info with the input Pateint Account Number
//verify the Patient Account number doesn't already exist
intResult = Array.BinarySearch(tempobjectPatient, SeekPatient);
//intResult = Array.BinarySearch(objectPatient, SeekPatient);
//if (objectPatient.Equals(SeekPatient)) //Can not get the .Equals to work at all...
if (intResult >= 0)
{
Console.Write("\nSorry, Patient Account Number {0} is a duplicate.", intInputValue);
Console.Write("\nPlease re-enter the Patient Account Number: ");
goto ReadTheAccountNumber;
}
else //no match found, get the rest of the data and create the object
{
if (x > 0) { Console.Write("***Patient Account Number unique and accepted***\n"); } //looks silly to display this if entering the first record
Console.Write("Enter the Patient Name: ");
strTempName = Console.ReadLine();
Console.Write("Enter the Patient Age: ");
intTempAge = Convert.ToInt32(Console.ReadLine());
Console.Write("Enter the total annual Patient amount due: ");
dblTempTotal = Convert.ToDouble(Console.ReadLine());
objectPatient[x] = new Patient(intInputValue, strTempName, intTempAge, dblTempTotal);
}
}
Here is the class:
class Patient : IComparable
{
//Data fields
private int patientNumber;
private string patientName;
private int patientAge;
private double patientAmountDue;
//Constructors
public Patient(): this(9,"ZZZ",0,0.00)
{
}
public Patient(int _patientNumber, string _patientName, int _patientAge, double _patientAmountDue)
{
PatientNumber = _patientNumber;
PatientName = _patientName;
PatientAge = _patientAge;
PatientAmountDue = _patientAmountDue;
}
//Properties
public int PatientNumber
{
get { return patientNumber; }
set { patientNumber = value; }
}
public string PatientName
{
get { return patientName; }
set { patientName = value; }
}
public int PatientAge
{
get { return patientAge; }
set { patientAge = value; }
}
public double PatientAmountDue
{
get { return patientAmountDue; }
set { patientAmountDue = value; }
}
//Interfaces
int IComparable.CompareTo(Object o)
{
int returnVal; //temporary value container
Patient temp = (Patient)o; //create temp instance of the class
if (this.PatientNumber > temp.PatientNumber)
returnVal = 1;
else
if (this.PatientNumber < temp.PatientNumber)
returnVal = -1;
else
returnVal = 0; //exact match
return returnVal;
}
}
You can put all the patient numbers into HashSet<int> and test via Contains() if a number is allocated one:
class Patient : IComparable {
...
// Simplest, not thread safe
private static HashSet<int> s_AllocatedPatientNumbers = new HashSet<int>();
public static Boolean IsNumberAllocated(int patientNumber) {
return s_AllocatedPatientNumbers.Contains(patientNumber);
}
public int PatientNumber {
get {
return patientNumber;
}
set {
s_AllocatedPatientNumbers.Remove(patientNumber);
patientNumber = value;
s_AllocatedPatientNumbers.Add(patientNumber);
}
}
}
So whenever you need to test if the number has been allocated you have no need to create a temporal patient, sort the array etc. just one simple call:
if (Patient.IsNumberAllocated(intInputValue)) {
...
}
This
Patient[] tempobjectPatient = new Patient[intMaxNum];
creates a new array and assigns it to tempobjectPatient. But this new array is never used, because here
tempobjectPatient = objectPatient;
you immediately assign the old one to tempobjectPatient. So after this, you don't have two array instances. Both tempobjectPatient and objectPatient refer to the same instance.
You probably want:
Patient[] tempobjectPatient = (Patient[])objectPatient.Clone();
Replace the Array with a Dictionary, it is desigend to be used with unique keys:
Dictionary<int,Patient> patients = new Dictionary<int,Patient>();
while (true)
{
Console.Write("\nEnter Patient Account Number: ");
int number = Convert.ToInt32(Console.ReadLine());
if (patients.ContainsKey(number))
{
Console.Write("\nSorry, Patient Account Number {0} is a duplicate.", number);
Console.Write("\nPlease re-enter the Patient Account Number: ");
continue;
}
Console.Write("***Patient Account Number unique and accepted***\n");
Console.Write("Enter the Patient Name: ");
string name = Console.ReadLine();
Console.Write("Enter the Patient Age: ");
int age = Convert.ToInt32(Console.ReadLine());
Console.Write("Enter the total annual Patient amount due: ");
double amountDue = Convert.ToDouble(Console.ReadLine());
patients.Add(number, new Patient(number, name, age, amountDue));
}
The loop is now missing an exit condition.
EDIT:
While this does not answer the question of the title i think it is what the OP was looking for.

Retrieve data from arrays

I want to enter students' Id and Marks into an array. Problem statement is that if user enters the Id of student then Mark of that student should be displayed. Below is my code so far. Could you please help me?
int [] mark = new int [5] ;
string [] studentsid = new string [5];
string userInput = "";
bool found = false;
int i = 0;
string[] answer = new string[5];
for (i = 0; i < answer.Length; i++)
{
Console.WriteLine("Enter Student " + (i + 1) + " 's ID Number: ");
studentsid[i] = Console.ReadLine();
Console.WriteLine("Enter student" + (i + 1) + "'s mark: ");
mark[i] = Convert.ToInt32(Console.ReadLine());
}
Console.WriteLine("Enter one of you student's id number");
userInput = Console.ReadLine();
if (studentsid[i].ToUpper() == userInput.ToUpper())
{
found = true;
Console.WriteLine(mark[i]);
}
if (mark[i] >=85 && mark[i] <= 100 )
{
Console.WriteLine("Distinction");
}
Console.ReadKey();
You need to put a second loop around the block of code that checks if the student's ID matches.
Right now, you are only checking if one student (the last student in the array) matches the user's input. You are also using a for-loop's control variable outside of the loop, which is generally considered bad practice.
Consider something like this:
Console.WriteLine("Enter one of you student's id number");
userInput = Console.ReadLine();
for (int i = 0; i < studentsid.length; i++)
{
if (studentsid[i].ToUpper() == userInput.ToUpper())
{
found = true;
Console.WriteLine(mark[i]);
}
}
Also, your "answers" array serves no purpose. You only create it to check its hard-coded length of 5. Use the length of studentsid instead.
Finally, two arrays aren't really the ideal way to store this type of data.
A map, using the student ID as the key and the mark as the value, would be a much more efficient way to store and access this data.

C# console application - commission calculator - how to use method within method

I'm new to C# and I have encountered some problems with my console application that I'm recently working on. I am trying to have 3 methods:
getsales to get the sales the user made, calcCom to calculate the commission for the sales and finally main to make them work and establish the program.
I'm having trouble to make those methods work with(in) each other.
After i entered all the sales, the program goes to the else-statement and tells me "invalid entry". Since i haven't really gotten to output the variables I didn't expect any kind of output, but I want the program to tell the user the commission and sale for each person.
Please excuse me if I misused any terms or words, like I said I am new to this language! :D
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication38
{
class Program
{
public static void getsales ()
{
string inputsales;
double total = 0;
double sale = 0;
for (int salecount = 1; salecount <= 3; ++salecount)
{
Console.WriteLine("Enter sale: ");
inputsales = Console.ReadLine();
sale = Convert.ToDouble(inputsales);
total = total + sale;
Console.WriteLine();
}
}
public static void calcComm ()
{
double total = 0;
double comm = 0;
comm = total * 0.2;
}
static void Main()
{
Console.WriteLine(" Sunshine Hot Tubs \n Sales Commissions Report\n");
char Letter;
string name;
const string name1 = "Andreas";
const string name2 = "Brittany";
const string name3 = "Eric";
string inputLetter;
Console.WriteLine("Please enter intial or type z to quit");
inputLetter = Console.ReadLine();
Letter = Convert.ToChar(inputLetter);
while (Letter != 'z')
{
if (Letter == 'a')
{
name = name1;
getsales();
calcComm();
}
if (Letter == 'b')
{
name = name2;
getsales();
calcComm();
}
if (Letter == 'e')
{
name = name3;
getsales();
calcComm();
}
else
{
Console.WriteLine("Invalid entry try again");
inputLetter = Console.ReadLine();
}
}
}
}
}
I think your problem is you need this:
if (Letter == 'a')
{
name = name1;
getsales();
calcComm();
}
else if (Letter == 'b')
{
name = name2;
getsales();
calcComm();
}
else if (Letter == 'e')
{
name = name3;
getsales();
calcComm();
}
else
{
Console.WriteLine("Invalid entry try again");
inputLetter = Console.ReadLine();
}
You also need to copy this code after the else block, at the very end of your while loop.
Console.WriteLine("Please enter intial or type z to quit");
inputLetter = Console.ReadLine();
Letter = Convert.ToChar(inputLetter);
Also, remove this line from inside the else block. It isn't needed.
inputLetter = Console.ReadLine();
You probably intended to display the commision on the console. Change your getsales and calcComm to look like this:
public static void getsales ()
{
string inputsales;
double total = 0;
double sale = 0;
for (int salecount = 1; salecount <= 3; ++salecount)
{
Console.WriteLine("Enter sale: ");
inputsales = Console.ReadLine();
sale = Convert.ToDouble(inputsales);
total = total + sale;
Console.WriteLine();
}
calcComm(total);
}
public static void calcComm (double total)
{
double comm = 0;
comm = total * 0.2;
Console.WriteLine(comm);
}
Then remove all calls to calcComm from the Main method.
The variable "total" is in the two methods and they do not persist the data that you are looking for between the two methods that you have defined. That is, the total variable in getSales() method is different from calcComm() method.
You should move this:
double total = 0;
outside of the two methods and put it within the class with a static scope. Like:
class Program
{
static double total;
Also, reinitialize total to zero within your getSales() method.
calcComm() doesn't do anything...
I think you might want to have some of your variables as global so that if they are modified by a method you can still retrieve their value, or even better pass them to the method and get them returned with the new values.
To declare global variables you should declare them inside the class Program but outside any method and then make sure that no other methods have variables with the same name

Categories