Read from file and find max c# - c#

I have started learning c# just over 3 months so for homework I have an exercise where i have to read from a text file with 50 names and marks. I have to find the name with the highest mark display it and save the result to a text file.This is what I have come up with.
namespace FINAL
{
class Program
{
public static string [] parts;
struct StudentDetails
{
public string name;
public string surname;
};
struct CourseWorkResults
{ public int mark;
};
static void Main(string[] args)
{
string [] Lines = System.IO.File.ReadAllLines(#"F:\FINAL\StudentExamMarks.txt");
foreach (string line in Lines)
{
string[] parts = line.Split();
}
StudentDetails[] Student = new StudentDetails[50];
CourseWorkResults[] Results = new CourseWorkResults[50];
GetName( ref Student ,ref parts );
FindMark ( ref Results , parts);
FindMax (ref Results);
}
static void GetName(ref StudentDetails[]Student, ref string[] parts)
{
for (int i = 0; i < Student.Length; i++ )
{
Student[i].name = parts[0];
Student[i].surname = parts[1];
}
}
static void FindMark ( ref CourseWorkResults[]Results, string [] parts )
{
for (int i=0; i< Results.Length;i++)
{
Results[i].mark = Convert.ToInt16(parts[2]);
}
}
static void FindMax( ref CourseWorkResults[]Results)
{
for (int i = 0; i < Results.Length; i++)
{
int max = 0;
max = Results[2].mark;
if (Results[i].mark > max)
{
max = Results[i].mark;
}
}
}
static void DisplayResults( ref CourseWorkResults[]Results, StudentDetails[]Student, int max)
{
Console.WriteLine(" The student with the highest score is {0} {1} :{2}", Student[0].name, Student[1].surname, max);
}
}
}
My problem is the GetName procedure , I can't pass by reference parts. What am I doing wrong?

The problem here is that a struct is an immutable value type. So when you write :
Student[i].name = parts[0];
You are just trying to modify a local a copy of Student[i]. Actually, each time you trying to access Student[i], it will always return a copied value instead the instance on the list.
A workaround is to have StudentDetails as class instead of struct
class StudentDetails
{
public string name;
public string surname;
};
The issue applies also for CourseWorkResults struct.
In addition, as arrays are already reference in .NET. You don't really need to pass it as reference unless you want it to reference another object. I.e, this :
static void GetName(StudentDetails[]Student, ref string[] parts)
{
for (int i = 0; i < Student.Length; i++ )
{
Student[i].name = parts[0];
Student[i].surname = parts[1];
}
}
will work as
static void GetName(ref StudentDetails[]Student, ref string[] parts)
{
for (int i = 0; i < Student.Length; i++ )
{
Student[i].name = parts[0];
Student[i].surname = parts[1];
}
}
Unless you add something like this:
static void GetName(ref StudentDetails[]Student, ref string[] parts)
{
Student = anotherStudentArray ; // Here the ref param is really needed as you want to reassign Student to a new array instance
for (int i = 0; i < Student.Length; i++ )
{
Student[i].name = parts[0];
Student[i].surname = parts[1];
}
}

Related

Returning array of objects from static function

I am making a basic Visual Studio project.
Easiest way to explain is to show the code.
using System;
using System.Collections.Generic;
namespace testing
{
class Program
{
static void Main(string[] args)
{
int amountOfCars = getAmountOfCars();
Car[] myCars = createCars(amountOfCars);
}
public static int getAmountOfCars (){
Console.WriteLine("Amount of Cars to enter");
int amountOfCars = Convert.ToInt32(Console.ReadLine());
return amountOfCars;
}
public static Car createCars(int amountOfCars)
{
Car[] myCars = new Car[amountOfCars];
for (int i = 0; i < amountOfCars; i++)
{
Console.WriteLine("Enter brand");
string brand = Convert.ToString(Console.ReadLine());
Console.WriteLine("Enter amount of wheels");
int amountOfWheels = Convert.ToInt32(Console.ReadLine());
Console.WriteLine("Enter amount of seats");
int amountOfSeats = Convert.ToInt32(Console.ReadLine());
myCars[i] = new Car(brand, amountOfWheels, amountOfSeats);
}
return myCars[amountOfCars];
}
}
}
This line
Car[] myCars = createCars(amountOfCars);
Throws the following error:
Cannot implicitly convert type testing.Car to testing.Car[]
So, I then tried this to convert over
Car[] myCars = (Car[]) createCars(amountOfCars);
But it still throws the error.
Essentially I am just trying to return the array of objects from createcar function, so that it can be used within the rest of the code.
What is the best practice to solve this?
You need to return an array from createCars():
public static Car[] createCars(int amountOfCars)
{
Car[] myCars = new Car[amountOfCars];
for (int i = 0; i < amountOfCars; i++)
{
Console.WriteLine("Enter brand");
string brand = Convert.ToString(Console.ReadLine());
Console.WriteLine("Enter amount of wheels");
int amountOfWheels = Convert.ToInt32(Console.ReadLine());
Console.WriteLine("Enter amount of seats");
int amountOfSeats = Convert.ToInt32(Console.ReadLine());
myCars[i] = new Car(brand, amountOfWheels, amountOfSeats);
}
return myCars;
}
The signature of the function is
public static Car createCars(int amountOfCars)
instead of
public static Car[] createCars(int amountOfCars)
Also return just the array
return myCars;
instead of
return myCars[amountOfCars]; // This returns only one object at the amountOfCars index in the myCars array.
Also, this will trigger the ArrayIndexOutOfBoundsException as the myCars is allocated for amountOfCars and array spans from myCars[0] to myCars[amountOfCars-1]
funcion
public static int[] addFirst(int value, int count, int[] test)
{
test = test.Where(val => val != value).ToArray();
Array.Resize(ref test, test.Length + 1);
for (int i = count-1; i > 0; i--)
{
test[i] = test[i-1];
}
test[0] = value;
return test;
}
declaracion
array= addFirst(value, array.Length, array);

No errors in Visual Studio, Code Executes but something not right C# (updated! problem in String.Insert)

I'm writing a program for schoolwork, that's supposed to generate a 10 000 "movie" list. A single "movie" consist of a string in a form "moviename year director". I say "movie" because movie name and director are supposed to be randomly generated with letters from a-z.
I wrote the following logic to generate one such "movie". Movie name and director are random letter combination in length between 4-10 charachters. Code gives no errors in visual studio, executes, but shows blank. If I wrote correctly, then this code should generate one such string and print it, yet console is blank.
Do while loop is there to check if, however unlikely, there is a double item in the List (this is for when I do the 10 000 version).
In short, I dont understand what am I doing wrong?
using System;
using System.Collections.Generic;
using System.Linq;
namespace Experiment
{
class Program
{
static void Main(string[] args)
{
Movies();
Console.ReadKey();
}
public static void Movies()
{
List<string> movieList = new List<string>();
bool check = false;
do
{
string movie = "";
for (int i = 0; i < GetNum(); i++)
{
movie.Insert(0, Convert.ToString(GetLetter()));
}
movie.Insert(0, " ");
movie.Insert(0, Convert.ToString(GetYear()));
movie.Insert(0, " ");
for (int i = 0; i < GetNum(); i++)
{
movie.Insert(0, Convert.ToString(GetLetter()));
}
if (movieList.Contains(movie))
{
check = false;
}
else
{
movieList.Add(movie);
check = true;
}
} while (check == false);
Console.WriteLine(movieList[0]);
}
public static Random _random = new Random();
public static char GetLetter()
{
int num = _random.Next(0, 26);
char let = (char)('a' + num);
return let;
}
public static int GetNum()
{
int num = _random.Next(4, 11);
return num;
}
public static int GetYear()
{
int num = _random.Next(1920, 2020);
return num;
}
}
}
Strings are immutable, so calling the Insert() method on the movie string doesn't do anything to the current movie variable. Instead it returns the new string.
You are however better off changing the movie type from string to StringBuilder, which is a dynamically allocated buffer of characters, so your example becomes:
using System;
using System.Text;
using System.Collections.Generic;
namespace sotest
{
class Program
{
static void Main(string[] args)
{
Movies();
Console.ReadKey();
}
public static void Movies()
{
List<string> movieList = new List<string>();
bool check = false;
do
{
StringBuilder movie = new StringBuilder();
for (int i = 0; i < GetNum(); i++)
{
movie.Insert(0, Convert.ToString(GetLetter()));
}
movie.Insert(0, " ");
movie.Insert(0, Convert.ToString(GetYear()));
movie.Insert(0, " ");
for (int i = 0; i < GetNum(); i++)
{
movie.Insert(0, Convert.ToString(GetLetter()));
}
if (movieList.Contains(movie.ToString()))
{
check = false;
}
else
{
movieList.Add(movie.ToString());
check = true;
}
} while (check == false);
Console.WriteLine(movieList[0]);
}
public static Random _random = new Random();
public static char GetLetter()
{
int num = _random.Next(0, 26);
char let = (char)('a' + num);
return let;
}
public static int GetNum()
{
int num = _random.Next(4, 11);
return num;
}
public static int GetYear()
{
int num = _random.Next(1920, 2020);
return num;
}
}
}
The problem is that you are using movie.Insert incorrectly.
If you read the documentation for String.Insert it says
https://learn.microsoft.com/en-us/dotnet/api/system.string.insert?view=netframework-4.8
Returns a new string in which a specified string is inserted at a
specified index position in this instance
public string Insert (int startIndex, string value);
So it returns a new String, it does not amend the existing one. So you would need to do.
movie = movie.Insert(0, Convert.ToString(GetYear()));
However, I must also say that using String.Insert in this way is not the best approach.
You should instead look at using the StringBuilder class. It is very efficient when you want to amend strings (which are immutable objects).
You might want to read parts of this to help you understand. If you scroll down then it also suggests StringBuilder.
https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/strings/
Insert() method is used to return a new string from the specified string at a specified index position. In your case, you are not capturing the updated string.
The best approach to solve this is through using StringBuilder object. Please note that StringBuilder object is much efficient rather than playing with immutable string.

Cannot permanently replace value in an array

I can't permanently replace the array members. When I change the value of String Clue, the string being displayed only displays the current value of clue. I think the problem us on the initialization of char[]. I tried to put them in other parts of the code but it produces error. Beginner here! Hope you can help me. Thanks! :)
private void clues(String clue)
{
int idx = numb[wordOn]+4;
char[] GuessHide = Words[idx].ToUpper().ToCharArray();
char[] GuessShow = Words[idx].ToUpper().ToCharArray();
for (int a = 0; a < GuessHide.Length; a++)
{
if (GuessShow[a] != Convert.ToChar(clue.ToUpper()))
GuessHide[a] = '*';
else
GuessHide[a] = Convert.ToChar(clue.ToUpper());
}
Guess(string.Join("", GuessHide));
}
Edited - because you initalize GuessHide at each call of calls in your code and you do not store its current state you basically reset it each time. Still, you can make some small changes in your code like this:
private static void clues(string clue, char[] GuessHide, char[] GuessShow)
{
for (int a = 0; a < GuessHide.Length; a++)
{
if (GuessShow[a] == Convert.ToChar(clue.ToUpper()))
{
GuessHide[a] = Convert.ToChar(clue.ToUpper());
}
}
Console.WriteLine(string.Join("", GuessHide));
}
Call it like this:
clues("p", GuessHide, GuessShow);
clues("a", GuessHide, GuessShow);
Initialise GuessShow and GuessHide in the outside code like this:
char[] GuessHide = new string('*', Words[idx].Length).ToCharArray();
char[] GuessShow = Words[idx].ToUpper().ToCharArray();
public class Program
{
static string[] Words;
static string[] HiddenWords;
public static void Main()
{
Words = new string[] { "Apple", "Banana" };
HiddenWords = new string[Words.Length];
for (int i = 0; i < Words.Length; i++)
{
HiddenWords[i] = new string('*', Words[i].Length);
}
Guess('P', 0);
Guess('a', 0);
Guess('A', 1);
Guess('N', 1);
Console.ReadLine();
}
private static void Guess(char clue, int idx)
{
string originalWord = Words[idx];
string upperedWord = Words[idx].ToUpper();
char[] foundSoFar = HiddenWords[idx].ToCharArray();
clue = char.ToUpper(clue);
for (int i = 0; i < upperedWord.Length; i++)
{
if (upperedWord[i] == clue)
{
foundSoFar[i] = originalWord[i];
}
}
HiddenWords[idx] = new string(foundSoFar);
Console.WriteLine(HiddenWords[idx]);
}
}

How To Read Data Into A List Structure in C#?

I am writing a program that uses a StudentStruct (structure) that consists of: studentID, studentName, and grades (which is a separate List). The program is supposed to read in a sequential text file and contains methods that allow the user to: Add Student, Add Grades (to a particular student), Change Student Name, and Delete Student. At the end of the program it is supposed to overwrite the previous file with the new changes made from the current program session.
My question is how do I read in data from the text file into the separate structure variables within the lists?
My text file looks like this:
00000,Mimzi Dagger,100,50,75,70,45,10,98,83
00001,Alexander Druaga,89,45,80,90,15,73,99,100,61
00002,Nicholas Zarcoffsky,100,50,80,50,75,100,100
00003,Kantmiss Evershot,50,100
Once the structure variables within the list are filled, how do I overwrite the file with the contents of the List structure in the same format as the above file?
Since I have multiple and varying amounts of grades, how would I achieve looping through and adding each grade to the grades list?
As you can maybe already tell, I am very new to c# and this is my first project. Any help would be greatly appreciated!
class Program
{
static string pathSource = #"C:\schoolfiles\StudentGrades.txt";
struct StudentStruct
{
public string studentID;
public string studentName;
public List<string> grades;
}
static void Main(string[] args)
{
StudentStruct student = new StudentStruct();
List<StudentStruct> myList = new List<StudentStruct>();
student.grades = new List<string>();
}
UPDATE: Here is what I have come up with so far:
for (int i = 0; i < fileLines.Length; i++)
{
output = fileLines[i];
string[] outputArray = output.Split(',');
student.grades = new List<string>();
student.studentID = outputArray[0];
student.studentName = outputArray[1];
for (int j = 2; j < outputArray.Length; j++)
{
student.grades.Add(outputArray[j]);
}
myList.Add(student);
UPDATE: The code above worked out wonderfully. Here is the top part of my code involving this question:
static void Main(string[] args)
{
//Declare variables
string output;
//Read the File into an array
string[] fileLines = File.ReadAllLines(PathSource);
StudentStruct student = new StudentStruct();
List<StudentStruct> myList = new List<StudentStruct>();
for (int i = 0; i < fileLines.Length; i++)
{
output = fileLines[i];
string[] outputArray = output.Split(',');
student.grades = new List<string>();
student.studentID = outputArray[0];
student.studentName = outputArray[1];
for (int j = 2; j < outputArray.Length; j++)
{
student.grades.Add(outputArray[j]);
}
myList.Add(student);
}
MainMenu(myList);
}
And then to add the list back to the file I did this:
static void ExitModule(List<StudentStruct> myList)
{
//Declare variables
string inputChoice = null;
string output = null;
System.IO.StreamWriter file = new System.IO.StreamWriter(PathSource);
Console.Clear();
Console.WriteLine("Are You Sure You Want To Exit The Program? Y/N");
inputChoice = Console.ReadLine();
if (inputChoice == "Y" || inputChoice == "y")
{
for (int i = 0; i < myList.Count; i++)
{
output = (myList[i].studentID + "," + myList[i].studentName);
for (int j = 0; j < myList[i].grades.Count; j++)
{
output += ("," + myList[i].grades[j]);
}
file.WriteLine(output);
}
file.Close();
Environment.Exit(0);
Since this is clearly homework, it may be a good time for you to learn about data structures. This will help you store the information you've read in from the text file efficiently, allowing you to write it out much more easily after modifying the records.
Here are a couple of other random pointers:
The System.IO.File.ReadLines(pathSource) method would be a good place to start reading each line of a text file
You should almost never be using a struct in C# (especially one that is mutable like in your example) unless you are well-versed in its semantics and purpose. Use a class instead
try this code. it's working for me
class Program
{
static string pathSource = #"C:\schoolfiles\StudentGrades.txt";
struct StudentStruct
{
public string studentID;
public string studentName;
public List<string> grades;
}
static void Main(string[] args)
{
StudentStruct student = new StudentStruct();
List<StudentStruct> myList = new List<StudentStruct>();
student.grades = new List<string>();
// get all lines from text file
string[] allLinesFromFile = File.ReadAllLines(pathSource);
// iterate through each line and process it
foreach(string line in allLinesFromFile)
{
// split each line on ','
string[] sections = line.Split(',');
student.studentID = sections[0];
student.studentName = sections[1];
// use this loop to add numbers to list of grades
for(int i =2; i < sections.Length; i++)
{
student.grades.Add(sections[i]);
}
// add this student to list
myList.Add(student);
// create new object of student
student = new StudentStruct();
student.grades = new List<string>();
}
}
A few things.
(1) I am not sure why your teacher/professor would have you use a struct here. A class is much more appropriate. I actually cringed when I first saw the code
(2) having something as a const also implies that it is static "pathSource"
(3) Properties should be capitalized "pathSource" -> "PathSource"
(4) You should separate responsibilities... Try not to put everything in Main
(5) You shouldn't have to create an instance of grades outside of StudentStruct, this can lead to errors. i.e. NullPointerException... basically the struct should be self containing.
(6) Everything should compile. I haven't tested it. If you have any issues. let me know.
Program {
private const string PathSource = #"C:\schoolfiles\StudentGrades.txt";
private struct StudentStruct
{
public string StudentID;
public string StudentName;
private List<string> _gradeList;
public List<string> GradeList
{
// if _gradeList is null create a new instance
get { return _gradeList ?? (_gradeList = new List<string>()); }
private set { _gradeList = value; }
}
}
private static void Main( string[] args )
{
var studentFile = File.ReadAllLines( PathSource );
// If you haven't learned about delegates or extensions methods...
// You can just change it to a foreach statement
var myList = studentFile.Select( GetStudent ).ToList();
// make sure we have everything
foreach (var studentStruct in myList)
{
Console.WriteLine( "{0} {1}", studentStruct.studentName, studentStruct.studentID );
foreach (var grade in studentStruct.GradeList) { Console.Write( grade + " " ); }
Console.WriteLine();
}
}
private static StudentStruct GetStudent( string line )
{
var student = new StudentStruct();
var studentDelimited = line.Split( ',' );
student.studentID = studentDelimited[ 0 ];
student.studentName = studentDelimited[ 1 ];
for ( int i = 2; i < studentDelimited.Length; i++ ) { student.GradeList.Add( studentDelimited[ i ] ); }
return student;
}
}

Command line arguments in c#

I am pretty new to c# . Got a problem with command line arguments. what i want to do is make use of the third cmd line argument and write to it. I have specified the path of the file I want to write into and other stuffs. But the question here is can i access the command line arguments(for eg; args[3]) from user defined functions? How do we do tat? below is my code.
public class Nodes
{
public bool isVisited;
public string parent;
public string[] neighbour;
public int nodeValue;
public Nodes(string[] arr, int nodeValue)
{
this.neighbour = new string[arr.Length];
for (int x = 0; x < arr.Length; x++)
this.neighbour[x] = arr[x];//hi...works??
this.isVisited = false;
this.nodeValue = nodeValue;
}
}
public class DFS
{
static List<string> traversedList = new List<string>();
static List<string> parentList = new List<string>();
static BufferBlock<Object> buffer = new BufferBlock<object>();
static BufferBlock<Object> buffer1 = new BufferBlock<object>();
static BufferBlock<Object> buffer3 = new BufferBlock<object>();
static BufferBlock<Object> buffer2 = new BufferBlock<object>();
public static void Main(string[] args)
{
int N = 100;
int M = N * 4;
int P = N * 16;
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
List<string> global_list = new List<string>();
StreamReader file = new StreamReader(args[2]);
string text = file.ReadToEnd();
string[] lines = text.Split('\n');
string[][] array1 = new string[lines.Length][];
Nodes[] dfsNodes = new Nodes[lines.Length];
for (int i = 0; i < lines.Length; i++)
{
lines[i] = lines[i].Trim();
string[] words = lines[i].Split(' ');
array1[i] = new string[words.Length];
dfsNodes[i] = new Nodes(words, i);
for (int j = 0; j < words.Length; j++)
{
array1[i][j] = words[j];
}
}
StreamWriter sr = new StreamWriter(args[4]);
int startNode = int.Parse(args[3]);
if (args[1].Equals("a1"))
{
Console.WriteLine("algo 0");
buffer.Post(1);
dfs(dfsNodes, startNode, "root");
}
else if (args[1].Equals("a2"))
{
Console.WriteLine("algo 1");
buffer1.Post(1);
dfs1(dfsNodes, startNode, "root",sr);
}
else if (args[1].Equals("a3"))
{
buffer3.Post(1);
List<string> visitedtList = new List<string>();
Console.WriteLine("algo 2");
dfs2(dfsNodes, startNode, "root", visitedtList,sr);
}
stopwatch.Stop();
Console.WriteLine(stopwatch.Elapsed);
Console.ReadLine();
}
public static void dfs(Nodes[] node, int value, string parent,StreamWriter sr1)
{
int id = (int)buffer.Receive();
sr1=new StreamWriter(arg
Console.WriteLine("Node:" + value + " Parent:" + parent + " Id:" + id);
sr1.Write("Node:" + value + " Parent:" + parent + " Id:" + id);
id++;
traversedList.Add(value.ToString());
buffer.Post(id);
for (int z = 1; z < node[value].neighbour.Length; z++)
{
if (!traversedList.Contains(node[value].neighbour[z]))
{
dfs(node, int.Parse(node[value].neighbour[z]), value.ToString(),sr1);
}
}
return;
}
public static void dfs1(Nodes[] node, int value, string parent, StreamWriter sr)
{
int id = (int)buffer1.Receive();
sr.Write("Node:" + value + " Parent:" + parent + " Id:" + id);
node[value].isVisited = true;
node[value].parent = parent;
id++;
buffer1.Post(id);
for (int z = 1; z < node[value].neighbour.Length; z++)
{
buffer2.Post(node[int.Parse(node[value].neighbour[z])]);
if (!isVisited())
{
dfs1(node, int.Parse(node[value].neighbour[z]), value.ToString(),sr);
}
}
return;
}
public static void dfs2(Nodes[] node, int value, string parent, List<string> visitedtList, StreamWriter sr)
{
int id = (int)buffer3.Receive();
sr.Write("Node:" + value + " Parent:" + parent + " Id:" + id);
id++;
visitedtList.Add(value.ToString());
buffer3.Post(id);
for (int z = 1; z < node[value].neighbour.Length; z++)
{
buffer2.Post(node[int.Parse(node[value].neighbour[z])]);
if (!visitedtList.Contains(node[value].neighbour[z]))
dfs2(node, int.Parse(node[value].neighbour[z]), value.ToString(), visitedtList,sr);
}
return;
}
public static bool isVisited()
{
Nodes node = (Nodes)buffer2.Receive();
return node.isVisited;
}
}
So the thing is I want to write the output of each dfs to the file specified as the command line argument. So can I have access to the args in the dfs, dfs1 methods??? Thank you.
You could either keep a static field to hold it, or just use Environment.GetCommandLineArgs().
Well, in its simplest form, just save it to use later
class Program
{
static string _fpath;
static void Main(string[] args)
{
// ...stuff
_fpath = args[3];
}
static void WriteFile()
{
using(var stream = File.Open(_fpath, ...))
{
// write to file
}
}
}
Not necessarily exactly how I would do it, but you get the idea.
Also, regarding this bit of code...
this.neighbour = new string[arr.Length];
for (int x = 0; x < arr.Length; x++)
this.neighbour[x] = arr[x];//hi...works??
You can simply write
this.neighbour = arr;
Ahh, the wonders of managed code :D. No need to copy elements across to the second array. Of course, you need to consider the fact that changes to elements in the argument array (arr) will be reflected in your internal array now.
It would be better to pass arguments into functions instead of relying on some "hidden" way to pass them.
Both static variable and GetCommandLineArgs are useful to pass them in hidden way (as pointed out in other answers). Drawbacks are harder to test (since need to set static shared dependency) and less clear for future readers that there is this hidden dependency.

Categories

Resources