Find the index of an object in an array with unknown objects - c#

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)

Related

C# Print an array of strings in the reverse order

so I have a problem where when I open my program and select 4 which is supposed to make it display all of the strings in the array but in reverse.
using System.Linq;
using System.Diagnostics;
using System.Runtime.CompilerServices;
namespace Homework3
{
class Program
{
static void Main(string[] args)
{
int optionIntOne;
int optionIntTwo;
int i;
int num = 0;
decimal k;
string[] names = {"Keyhan", "Adolf", "Niklas", "Jenny",
"Elisa", "Rudolf", "Bengt", "Hassan", "Kajsa", "Maggan"};
Console.WriteLine("Please Select an Option Below:\n1 - Find a student by Index \n" +
"2 - Find student Index by name\n3 - Display all student names with their Index \n" +
"4 - Display all the students in reverse order");
string optionStringone = Console.ReadLine();
bool isNumericOne = Int32.TryParse(optionStringone, out optionIntOne);
Console.Clear();
switch (optionIntOne)
{
case 1:
Console.WriteLine("***FIND Student by ID" + "\n" + "Please select a number between 0 - 9:");
string studentNumber = (Console.ReadLine());
bool isNumericTwo = Int32.TryParse(studentNumber, out optionIntTwo);
if (isNumericTwo == true && optionIntTwo < names.Length)
Console.WriteLine(names[optionIntTwo]);
else
Console.WriteLine("Please enter a valid number");
break;
case 2:
Console.WriteLine("*** FIND Student Index by Name \n Please Print one of the above names");
foreach (string name in names)
Console.WriteLine((name));
Console.WriteLine("\n");
string studentName = (Console.ReadLine().ToUpper());
//ToUpper så slipper loopen göra det på repeat
Console.Clear();
bool b = false;
if (decimal.TryParse(studentName, out k) || studentName == string.Empty)
{
Console.WriteLine("Please Enter a Valid Name");
}
else
{
for (i = 0; i < names.Length; i++)
{
if (names[i].ToUpper() == studentName)
{
b = true;
Console.WriteLine(names[i] + " Has the index of " + i);
break;
}
}
if (b == false)
Console.WriteLine("The Student does not Exist in the List");
}
break;
case 3:
while (num < names.Length)
{
Console.WriteLine(num + " - " + names[num]);
num++;
}
break;
case 4:
while (num < names.Length)
{
Console.WriteLine(names[num].Reverse());
num++;
}
break;
default:
Console.WriteLine("Enter a Valid Number");
break;
}
Console.ReadKey();
}
}
}
(option 3 but to print them in reverse order)
Visual Studio isnt giving me any errors but it outputs like this 1 instead of the reverse order of this 2
Anyone know how to fix?/could point out the problem
First of all this line:
Console.WriteLine(names[num].Reverse());
Is reversing the strings inside the list (so names[0].Reverse() would be "nahyeK") because string is also a list (a list of chars). If you want to reverse the whole list you only write names.Reverse();
read more at: Microsoft docs - Reverse()
So the next step would be to print it out.
When you do list.Reverse(); it will return IEnumerable<TSource> which you can use in a loop to get the names.
string[] names = {"Keyhan", "Adolf", "Niklas", "Jenny",
"Elisa", "Rudolf", "Bengt", "Hassan", "Kajsa", "Maggan"};
IEnumerable<string> reversedList = names.Reverse();
foreach(string name in reversedList)
{
Console.WriteLine(name);
}
I usually use list.Reverse().ToList() so i get a "normal" list again because it's in my opinion easier to use/understand.
There is some options, the namespace system.linq.enumerable got some stuff for that:
.ToArray() - converts it to an Array.
.ToList() - converts it to a List
So the complete solution would be to keep the .Reverse outside the loop like this:
foreach(name in names.Reverse().ToList())
{
Console.WriteLine(name);
}
Here is one option:
static void Main(string[] args)
{
int num = 0;
string[] names = { "Alice", "Bob", "Charles", "Delilah" };
while (num < names.Length)
{
Console.WriteLine(names.Reverse().ElementAt(num));
num++;
}
// Prints
//
// Delilah
// Charles
// Bob
// Alice
Console.ReadKey();
}
As per the comments it is not optimal to call Reverse() in each loop. Instead, perform the Reverse() outside of the loop.
A better alternative might be:
static void Main(string[] args)
{
string[] names = { "Alice", "Bob", "Charles", "Delilah" };
for (int num = names.Length - 1; num >= 0; num--)
{
Console.WriteLine(names[num]);
}
Console.ReadKey();
}

How to output data in a certain form?

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();

Why cant I return the index of a value I have searched for in an array?

I have code to ask the user to enter a search value, which should then be compared against every value in the array.
If it is found then it should return every position it is found at in the array.
However when I enter a value which I know should be in the array the code will still say that it isn't.
static void Main(string[] args)
{
string[] low256 = File.ReadAllLines(#"C:\Users\Dingo Ice\Downloads\Low_256.txt");
Console.WriteLine("Choose a file to use, enter 1: Low, 2: High or 3: Mean");
int choice = Convert.ToInt32(Console.ReadLine());
//Sort Arrays into ascending order
Array.Sort(low256);
//Sort Arrays into descending order
Array.Reverse(low256);
if (choice == 1)
{
for (int i = 0; i < low256.Length; i++)
{
if (i % 10 == 0)
{
Console.WriteLine(low256[i]);
}
}
}
else
{
Console.WriteLine("Enter 1, 2 or 3");
}
Console.WriteLine("Enter an exact value to search for in the selected array");
string searchValue = Console.ReadLine();
int found = 0;
int counter = 0;
if (choice == 1)
{
foreach (var value in low256)
{
if (searchValue == value)
{
Console.WriteLine("Search value found at position: " + value);
found = 1;
}
else if (found == 0 && counter >= low256.GetUpperBound(0))
{
Console.WriteLine("The search value was not found in the selected array");
}
counter += 1;
}
}
Console.ReadLine();
}
Can you change this:
if (searchValue == value)
To:
if (searchValue.ToLower().Trim() == value.ToLower().Trim())
And see if this causes a match to be found?
Also, you mentioned that you want every position found to be returned, but in your example this doesn't happen as you only mention via a Console.WriteLine that it is found (but not even on which index). I have changed your log to the Console to include at which index it was found and the index gets added to a list of indexes found.
Does this solve your question?
static void Main(string[] args)
{
string[] low256 = File.ReadAllLines(#"C:\Users\Dingo Ice\Downloads\Low_256.txt");
Console.WriteLine("Choose a file to use, enter 1: Low, 2: High or 3: Mean");
int choice = Convert.ToInt32(Console.ReadLine());
//Sort Arrays into ascending order
Array.Sort(low256);
//Sort Arrays into descending order
Array.Reverse(low256);
if (choice == 1)
{
for (int i = 0; i < low256.Length; i++)
{
if (i % 10 == 0)
{
Console.WriteLine(low256[i]);
}
}
}
else
{
Console.WriteLine("Enter 1, 2 or 3");
}
Console.WriteLine("Enter an exact value to search for in the selected array");
string searchValue = Console.ReadLine();
searchValue = searchValue.ToLower().Trim();
List<int> indexes = new List<int>();
if (choice == 1)
{
for (int i = 0; i < low256.Length; i++)
{
string value = low256[i].ToLower().Trim();
if (searchValue == value)
{
Console.WriteLine($"Search value '{value}' found at position: '{i}'.");
indexes.Add(i);
}
}
if (indexes.Count == 0)
{
Console.WriteLine("The search value was not found in the selected array");
}
}
Console.ReadLine();
}
You could do this with LINQ by projecting your array into a ValueTuple with Select, using Where to filter, then selecting the index.
Note : The string.Compare method just gives you the option on how you want to compare the strings, remove or change the option to taste
var ary1 = new string[] { "asd", "sdf", "sdf", "sgfdfg", "dsf", "asd" };
var someValue = "asd";
var indices = ary1.Select((value, index) => (value, index)) // convert to value index tuple
.Where(pair => string.Compare(pair.value, someValue,CultureInfo.CurrentCulture, CompareOptions.OrdinalIgnoreCase) == 0)
.Select(pair => pair.index);
foreach(var index in indices)
Console.WriteLine(index);
Output
0
5
Maybe you are looking for Array.IndexOf method.
private void button1_Click(object sender, EventArgs e)
{
string[] names = { "Mathew", "Mark", "Luke", "John" };
int index = Array.IndexOf(names, "Luke");
Debug.Print(index.ToString());
//Prints 2
}

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.

how do I return back after catching an error?

for (int loopCount = 0; loopCount < 9; loopCount++)
{
Console.WriteLine("Employee #" + (loopCount + 1) + " first name: ");
fullName[loopCount, 0] = Console.ReadLine().ToLower();
// first name
if (fullName[loopCount, 0].Length == 0)
{
giveError();
// nothing typed warning
}
Console.WriteLine("Employee #" + (loopCount + 1) + " second name: ");
fullName[0, loopCount] = Console.ReadLine().ToLower();
// second name
}
How do I return back to adding a first name if the user enters nothing without moving on to the next loop?
Wrap your logic around a do while loop:
string name = null;
do
{
// Read input from user
}
while(!IsLegal(name));
Without changing your loop structure, you can add the following lines after giveError(); inside you if statment:
loopcount--;
continue;
This will decrement your loopcount, so you do not loose your place and will return your program to the top of the loop and then re-increment loopcount.
You can initialize an empty value before you ask for input, and then wrap the input code in a while loop that continues asking until a valid value is given:
string fullname = string.Empty;
while(string.IsNullOrEmpty(fullname))
{
fullname = Console.ReadLine().ToLower();
}
fullName[loopCount, 0] = fullname;
This way, if the user doesn't type anything, you'll just loop back and ask again.
Assuming fullName is a string[9,2]:
for (int loopCount = 0; loopCount < 9; loopCount++)
{
Console.WriteLine("Employee #" + (loopCount + 1) + " first name: ");
fullName[loopCount, 0] = Console.ReadLine().ToLower();
while(fullName[loopCount, 0].Length == 0)
{
Console.WriteLine("Bad Input, Retry");
fullName[loopCount, 0] = Console.ReadLine().ToLower();
}
}
It's worth the extra input line before the loop, so that if the loop is needed (i.e. bad input is received) you can supply a "bad input" message.
for your second name, you want fullName[loopCount,1], not fullName[0,loopCount]

Categories