Function to calculate grades from multiple files

Answered Function to calculate grades from multiple files

  • Thursday, April 12, 2012 10:32 PM
     
      Has Code

    Hello all,

    I am working on a program that calculates students' grades in class using information fed in from outside files. There are two master .txt files. One contains the categories in the class(exam, lab, etc.), how much they account for in the final grade, and how many items in each category there are. The other .txt file contains the student roster, which includes the student ID, last name, and first name. Each ID number has a corresponding .data file, with lines in them that looks like this:

    E,1,97

    H,2,81

    I'm in the home stretch of getting this program to work. I am one function short of sweet victory. Here is everything I have so far, with comments on what each one is doing:

    using System; using System.IO; using System.Collections.Generic; namespace GradeProgram { class Gradebook { static void Main(string[]args){ Console.WriteLine("Please enter the course abbreviation: "); string courseAbbreviation = Console.ReadLine(); string categoryFileName = "Categories" + courseAbbreviation + ".txt"; var reader = new StreamReader(categoryFileName); while(!reader.EndOfStream){ reader.ReadToEnd(); } reader.Close(); string studentFileName = "Students" + courseAbbreviation + ".txt"; var reader2 = new StreamReader(studentFileName); while(!reader2.EndOfStream){ reader2.ReadToEnd(); } reader2.Close(); /*These two Streamreaders go and fetch the two master .txt files that match the course

    abbreviation the user supplied.*/ /*string [] category_names = getClassCategories(categoryFileName); int [] category_weights = weightValues(categoryFileName); int [] item_numbers = itemNumbers(categoryFileName);*/ //This was to check to see if three of my functions below did what they were supposed to do. They work. string [] student1 = getStudentRoster(studentFileName,1); string [] student2 = getStudentRoster(studentFileName,2); string [] student3 = getStudentRoster(studentFileName,3); /*This string cluster was to check if my getStudentRoster function works. What it ended up doing was

    printing the students' ID, last name, and first name in three arrays(one for each student),

    each containing three elements.*/ List<string>studentID = new List<string>(); List<string[]> student_list = new List<string[]>(); student_list.Add(student1); student_list.Add(student2); student_list.Add(student3); foreach(string [] s in student_list){ studentID.Add(s[0]); } foreach(string id in studentID){ string testStudentFile = id + courseAbbreviation + ".data"; var reader3 = new StreamReader(testStudentFile); while(!reader3.EndOfStream){ string line = reader3.ReadLine(); Console.WriteLine(line); } reader3.Close(); } /*These Lists and foreach loops put all the student's information into lists, then cycles through them

    and picks out JUST the students' ID number. That ID number is then used to access

    the corresponding .data file, which has every single grade for that student.*/

    }//closes main static string[] getStringArray(string input) { string[] parts = input.Split(','); return parts; } static int[] getIntArray(string input) { string[] parts = input.Split(','); int[] intparts = new int[parts.Length]; for (int i=0; i < parts.Length; i++) intparts[i] = int.Parse(parts[i]); return intparts; } //These two functions are here to help organize information into arrays. static string[] getClassCategories(string categoryFileName) { var reader = new StreamReader(categoryFileName); string categories = reader.ReadLine(); string[] catnames = getStringArray (categories); for (int i = 0; i < catnames.Length; i++){ Console.WriteLine ("Category name at position {0} = {1}", i, catnames [i]); string firstLetters = catnames[i]; string first_letter = firstLetters.Substring(0,1); Console.WriteLine("Letter at position {0} = {1}", i, first_letter); } return catnames;

    }

    /*This function takes the category names from the Categories.txt file and puts them into an array.

    Also, it takes the first letter of each category name and puts that into an array.*/

    static int[] weightValues(string categoryFileName) { int targetLine = 2; int counter = 1; int[] weight_values = new int[0]; var reader = new StreamReader (categoryFileName); while (!reader.EndOfStream) { string weights = reader.ReadLine(); if (counter == targetLine) { weight_values = getIntArray(weights); } counter++; } for (int i = 0; i < weight_values.Length; i++) { Console.WriteLine("Weight value at position {0} = {1}", i, weight_values[i]); } return weight_values; } //This function takes the weight values from the Categories.txt file and puts them into an array. static int[] itemNumbers(string categoryFileName) { int targetLine = 3; int counter = 1; int[] item_values = new int[0]; var reader = new StreamReader(categoryFileName); while (!reader.EndOfStream) { string items = reader.ReadLine(); if (counter == targetLine){ item_values = getIntArray(items); } counter++; } for (int i = 0; i < item_values.Length; i++) { Console.WriteLine ("Number of items at position {0} = {1}", i, item_values [i]); } return item_values; } //This function takes the number of items for each category and puts them into an array. static string[] getStudentRoster(string studentFileName, int targetLine) { int counter = 1; string[] next_Student = new string [0]; var reader = new StreamReader (studentFileName); while (!reader.EndOfStream) { string item = reader.ReadLine(); if (counter == targetLine) { next_Student = getStringArray(item); } counter++; } for (int i=0; i < next_Student.Length; i++){ Console.WriteLine ("Student at position {0} = {1}", i, next_Student[i]); } return next_Student; } /*This function takes the students' ID number, last name, and first name from the Students.txt file and puts

    each student into an array, which contains three elements.*/ }//closes class }//closes namespace

    So here's the meat and bones of my problem. I need a function that can calculate these students' final grades. My problem is the information is scattered everywhere. Here's what I know what it needs to do for sure:

    This function needs the first letter of every category (which is already in an array), so when it sees the letter H in a student's record for example, it will know to add that grade to the running total for the homework portion of the final grade. I have a piece of code here that I think is supposed to help do that, but I'm not entirely sure:

    static int codeIndex(string code, string[] categories)
    {
       for (int i = 0; i < categories.Length; i++) {
          if (categories[i].StartsWith(code)) {
             return i;
          }
       }
       return -1;
    }

    The same process needs to be done for every category for every student. I have no idea where to even begin with this monster function, and I'm so close to the end, I can feel it. In the immortal words of The Beatles: won't you please please help me? :D

All Replies

  • Thursday, April 12, 2012 10:40 PM
     
      Has Code

    > var reader = new StreamReader(categoryFileName); while(!reader.EndOfStream) [...] reader.Close();
     

    if you need to read a text file, just use the code below instead of code above

    var text = System.IO.File.ReadAllText(categoryFileName);


    and please provide a samples of all .txt files.
     
     


    • Edited by Malobukv Thursday, April 12, 2012 11:03 PM
    •  
  • Thursday, April 12, 2012 11:01 PM
     
     

    Here's an observation: Main opens categoryFileName, reads all off, ignores what it has read, closes the file. Why?

    BTW Don't know how you pasted the code but copying it and pasting it shows no lines breaks. So it's a pain to get into C# to do some checking.


    Regards David R
    ---------------------------------------------------------------
    The great thing about Object Oriented code is that it can make small, simple problems look like large, complex ones.
    Object-oriented programming offers a sustainable way to write spaghetti code. - Paul Graham.
    Every program eventually becomes rococo, and then rubble. - Alan Perlis
    The only valid measurement of code quality: WTFs/minute.

  • Thursday, April 12, 2012 11:09 PM
     
     

    OK. I'm aware my code's a little messy but I'll clean it up once everything's working.

    Samples of all files? Alright, here's what I have:

    Categories.txt Master File (category names, weights, number of items in each):

    Exam,Lab,Homework,Project,Class Participation 
    35, 10, 10, 25, 20
    3, 5, 5, 1, 4

    Students.txt Master File (ID, last name, first name):

    P32465878,Fa,Mulan
    P58678454,Eadig,Eomer
    P21354785,Stark, Eddard

    P32465878.data file:

    E,1,81
    H,1,87
    L,4,97
    C,1,100
    H,2,83
    E,3,94
    C,2,100
    L,1,89
    H,4,75
    P,1,95
    C,3,100
    H,5,84
    E,2,91
    H,3,99
    C,4,100
    L,2,88
    L,3,90
    L,5,96


    P58678454.data file: 

    E,1,99
    E,2,87
    E,3,75
    L,1,85
    L,2,98
    L,3,80
    L,4,83
    L,5,97
    H,1,92
    H,2,83
    H,3,72
    H,4,97
    H,5,81
    P,1,89
    C,1,100
    C,2,100
    C,3,100
    C,4,100

    P21354785.data file:

    C,1,100
    C,2,100
    C,3,100
    C,4,100
    L,1,80
    L,2,86
    L,3,83
    L,4,85
    L,5,99
    P,1,93
    H,1,98
    H,2,84
    H,3,89
    H,4,77
    H,5,92
    E,1,88
    E,2,92
    E,3,80

    Is that something along the lines of what you wanted?

  • Thursday, April 12, 2012 11:29 PM
     
     

    I wasn't even aware it did that. I thought that since all the information was printed back to me in an array, it was doing something with the file. 

    Sorry about the lack of line breaks...I'm still getting the hang of incorporating source code into forum posts and didn't realize how badly it turned out.

  • Thursday, April 12, 2012 11:41 PM
     
     

    It looks like you are because you open and read them later on. The first two streamreaders stuff is effectively though.

    A couple of other comments:

    I find comments placed before the code they refer to better than after - more conventional.

    You get a courseAbbreviatiion and use it to open txt files. Does that mean there is actually multiple category and student files?

    Is there a reason for using arrays and not to use classes and Lists? I'd look to have a class for each main item (category, student, grade) and have a list of categories, list of students each student having a list  of grades - that's a first thought without looking at the detail. Then I'd have methods to populate each list. Once that is done I can have methods that manipulate the lists however I like. You could do the same with arrays (grades one might be better as multidimensional) but messy to keep them in synch with each other.


    Regards David R
    ---------------------------------------------------------------
    The great thing about Object Oriented code is that it can make small, simple problems look like large, complex ones.
    Object-oriented programming offers a sustainable way to write spaghetti code. - Paul Graham.
    Every program eventually becomes rococo, and then rubble. - Alan Perlis
    The only valid measurement of code quality: WTFs/minute.

  • Thursday, April 12, 2012 11:52 PM
     
     

    Sorry about the comment confusion as well...still a newbie at C#, and programming overall.

    There are only two master text files. I do however have three .data files (one for each individual student).

    The only reason for using arrays and not classes and Lists is because I didn't know about classes and Lists until recently. I've been working on this program for like a month now and the only background I had when I started for organizing information was with arrays. Other people I've conferred with said they were trying to use 2D arrays, but I was also told it was possible to use 1D arrays. 

  • Friday, April 13, 2012 12:10 AM
     
      Has Code
    > Is that something along the lines of what you wanted?


    yes. try using the following code:

    using System.IO;
    using System.Linq;
    using System.Windows.Forms;
    
    namespace WindowsFormsApplication1
    {
        public partial class Form1 : Form
        {
            public Form1()
            {
                var dir = @"C:\Temp\";    // path to the folder with your .txt files
                var students = (from line in File.ReadLines(dir + "students.txt")
                                where string.IsNullOrWhiteSpace(line) == false
                                let arr = line.Split(',')
                                let id = arr[0]
                                let data = File.ReadLines(dir + id + ".txt").Select(d =>
                                                {
                                                    var itms = d.Split(',');
                                                    return new { A = itms[0], B = int.Parse(itms[1]), C = int.Parse(itms[2]) };
                                                })
                                select new
                                {
                                    ID = id,
                                    LastName = arr[1],
                                    FirstName = arr[2],
                                    Data = data.ToArray()
                                });
    
                var tb = new RichTextBox { Parent = this, Dock = DockStyle.Fill };
                foreach (var s in students)
                {
                    tb.AppendText(s.FirstName + " " + s.Data.Sum(a => a.C) + "\n");
                }
            }
        }
    }

      
    • Edited by Malobukv Friday, April 13, 2012 12:12 AM
    •  
  • Friday, April 13, 2012 12:10 AM
     
     

    Comment confusion is no a show stopper - met it a few times before but always throws me. It's just sooo unconventional. LOL.

    It can be done with arrays but I think it is more complicated - e.g. if you take the categories you need three arrays: name, weight, items. Same for students: ID, last name, first name. Unless you use multidimensional ones, and you can only do that if the types (i.e. string, int, double or whatever are the same). If you only have to deal with one student at a time it's a bit easier. But if you have to deal with all students e.g. to get class average it gets messier.


    Regards David R
    ---------------------------------------------------------------
    The great thing about Object Oriented code is that it can make small, simple problems look like large, complex ones.
    Object-oriented programming offers a sustainable way to write spaghetti code. - Paul Graham.
    Every program eventually becomes rococo, and then rubble. - Alan Perlis
    The only valid measurement of code quality: WTFs/minute.

  • Friday, April 13, 2012 12:22 AM
     
     

    I'm noticing that it's complicated....if I were old enough to drink my liver would probably be in a cirrhotic state by now. 

    That's basically the gist of what I have now. I have three arrays for names, weights, items. The students each have their own arrays. So I think I'm pretty much set up to do one student at a time, just the whole keeping a running total for all the categories for all the students and ending up with plausible grades is really throwing me. I don't need a whole class average, I just need one final average for each student, kinda like a report card.

     
  • Friday, April 13, 2012 1:28 AM
     
     

    @Malobukv

    Wow. You definitely did not have to do that for me. I'm sure it works just as you designed it. Unfortunately I can't use it the want you want me to because I'm not advanced enough in C# to implement it properly. But thank you so much for putting something forward. :D

  • Friday, April 13, 2012 8:45 AM
     
     

    Malobukv

    It has some minor bugs (e.g. ReadLines should be ReadAllLines) that are easily fixed. (Could they be due to you using 2010and I'm using 2008?). Gives the following output:

    Mulan 1649
    Eomer 1618
    Eddard 1626

    Which is not exactly what OP wants. As a starting point it's good but it ignores the categories.

    I'm not a great fan of LINQ - dislike things like the 'Var students = ..'. statement, they so easily border on being incomprehensible. And it reminds me too much of SQL stored procedures that have got out of hand. LOL.


    Regards David R
    ---------------------------------------------------------------
    The great thing about Object Oriented code is that it can make small, simple problems look like large, complex ones.
    Object-oriented programming offers a sustainable way to write spaghetti code. - Paul Graham.
    Every program eventually becomes rococo, and then rubble. - Alan Perlis
    The only valid measurement of code quality: WTFs/minute.

  • Friday, April 13, 2012 12:20 PM
     
     
    > Wow. You definitely did not have to do that for me. I'm sure it works just as you designed it.
     
     
    you are welcome. 
    notice, the sample above just loads text files and shows how to use LINQ to calculate results.

  • Friday, April 13, 2012 12:25 PM
     
     
    @Riced

    > It has some minor bugs (e.g. ReadLines should be ReadAllLines) that are easily fixed. (Could they be due to you using 2010and I'm using 2008?).


    when you use ReadLines, you can start enumerating the collection of strings before the whole collection is returned.
    see File.ReadLines Method (String) - .NET Framework 4
     
     
  • Friday, April 13, 2012 12:36 PM
     
     

    Malobukv

    As I thought - different versions of Framework. ReadLines not in 3.5 (VS 2008) so my speculation was right. :) 


    Regards David R
    ---------------------------------------------------------------
    The great thing about Object Oriented code is that it can make small, simple problems look like large, complex ones.
    Object-oriented programming offers a sustainable way to write spaghetti code. - Paul Graham.
    Every program eventually becomes rococo, and then rubble. - Alan Perlis
    The only valid measurement of code quality: WTFs/minute.

  • Monday, April 16, 2012 5:44 AM
     
     Answered Has Code

    Hi Ip0onfire,

    I wrote a simple sample for you. Please check it:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.IO;
    using System.Xml.Serialization;
    using System.Runtime.Serialization.Formatters.Binary;
    
    namespace GradeProgram
    {
        [Serializable()]//Marked as serializable
        public class Student
        {
            private String StudentID;
            private String FirstName;
            private String LastName;
            internal List<Grade> StudentGrade = new List<Grade>();
            internal String ID
            {
                set { StudentID = value; }
                get { return StudentID; }
            }
            internal String FName
            {
                set { FirstName = value; }
                get { return FirstName; }
            }
            internal String LName
            {
                set { LastName = value; }
                get { return LastName; }
            }
            internal Student(String id, String firstname, String lastname)
            {
                StudentID = id;
                FirstName = firstname;
                LastName = lastname;
            }
        }
        [Serializable()]
        public class Category
        {
            private String CategoryName;
            private Int32 CategoryWeights;
            private Int32 CategoryNumbers;
            internal String CName
            {
                set { CategoryName = value; }
                get { return CategoryName; }
            }
            internal Int32 CWeights
            {
                set { CategoryWeights = value; }
                get { return CategoryWeights; }
            }
            internal Int32 CNumbers
            {
                set { CategoryNumbers = value; }
                get { return CategoryNumbers; }
            }
            internal Category() { }
            internal Category(String name, Int32 weights, Int32 numbers)
            {
                CategoryName = name;
                CategoryWeights = weights;
                CategoryNumbers = numbers;
            }
        }
        [Serializable()]
        internal class Grade
        {
            private String Category;
            private Int32 Number;
            private Int32 StudentGrade;
            private Int32 Weights;
            internal String CName
            {
                set { Category = value; }
                get { return Category; }
            }
            internal Int32 GNumber
            {
                set { Number = value; }
                get { return Number; }
            }
            internal Int32 StuGrade
            {
                set { StudentGrade = value; }
                get { return StudentGrade; }
            }
            internal Int32 GWeights
            {
                set { Weights = value; }
                get { return Weights; }
            }
            internal Grade(String category, Int32 number, Int32 grade, Int32 weights)
            {
                Category = category;
                Number = number;
                StudentGrade = grade;
                Weights = weights;
            }
        }
        class Gradebook
        {
            static List<Category> CategoryList = new List<Category>();
            static List<Student> StudentList = new List<Student>();
            static Category Exam = new Category("Exam", 35, 3);
            static Category Lab = new Category("Lab", 10, 5);
            static Category Homework = new Category("Homework", 10, 5);
            static Category Project = new Category("Project", 25, 1);
            static Category ClassParticipation = new Category("ClassParticipation", 20, 4);
    
            static void Main(string[] args)
            {
                CategoryList.Add(Exam);
                CategoryList.Add(Lab);
                CategoryList.Add(Homework);
                CategoryList.Add(Project);
                CategoryList.Add(ClassParticipation);
    
                Student P32465878 = new Student("P32465878", "Fa", "Mulan");
                P32465878.StudentGrade.Add(new Grade(CategoryList[0].CName, 1, 81, CategoryList[0].CWeights));
                StudentList.Add(P32465878);//Initialize a Student object.
    
                WriteToFile();
                ReadFromFile();
                Console.ReadLine();
    
            }
            internal Int32 GradeCalculation(Student stu)
            {
                Int32 Result = 0;
                foreach (Grade StuGrade in stu.StudentGrade)
                {
                    Result += StuGrade.StuGrade * StuGrade.GWeights;//Add your way to calculate the final grades.
                }
                return Result;
            }
            static void WriteToFile()
            {
                FileStream streamCategory = new FileStream("Category.bin", FileMode.Create);
                BinaryFormatter bin = new BinaryFormatter();
                bin.Serialize(streamCategory, CategoryList);
                streamCategory.Close();
                FileStream streamStudent = new FileStream("Student.bin", FileMode.Create);
                bin.Serialize(streamStudent, StudentList);
                streamStudent.Close();
            }
            static void ReadFromFile()
            {
                FileStream streamCategory = new FileStream("Category.bin", FileMode.Open);
                BinaryFormatter bin = new BinaryFormatter();
                List<Category> CategoryList2 = new List<Category>();
                CategoryList2 = (List<Category>)bin.Deserialize(streamCategory);
                foreach(Category cate in CategoryList2)
                {
                    Console.WriteLine(cate.CName);
                }
                streamCategory.Close();
    
                FileStream streamStudent = new FileStream("Student.bin", FileMode.Open);
                List<Student> StudentList2 = new List<Student>();
                StudentList2 = (List<Student>)bin.Deserialize(streamStudent);
                foreach (Student stud in StudentList2)
                {
                    Console.WriteLine(stud.FName);
                }
                streamStudent.Close();
            }
        }
    }

    In this sample, I use classes instead of lists to describe the objects, and use some lists to store the instances of class. Since you want to save the data, I wrote two method to explain how to save lists to a file and read from a file.

    I hope it can help you.

    Best Regards,