Need advice on how to handle grades for a student

  • (3 Pages)
  • +
  • 1
  • 2
  • 3

36 Replies - 2049 Views - Last Post: 20 July 2020 - 08:00 AM Rate Topic: -----

#1 capsyl   User is offline

  • New D.I.C Head

Reputation: 1
  • View blog
  • Posts: 49
  • Joined: 17-June 20

Need advice on how to handle grades for a student

Posted 11 July 2020 - 05:52 AM

I'm currently having an array that hold grades in my student object. But would like to have each grade with a label/subject as well.
What would be the preferred way of doing this? Creating a new object for grades or have some sort of multidimensional array?

What I want to achieve:
        Name            Gender      Age         Art     Biology     History     Math    Music
        Ben Nixon       Man         20          C       B           A           F       C



My current student object that holds an array of grades with no labels:
class Student
    {
        // Attributes
        private string name;
        private string gender;
        private int age;
        private char[] grades;

        // Properties
        public string Name      { get { return name; } set { name = value; } }
        public int Age          { get { return age; } set { age = value; }  }
        public string Gender    { get { return gender; } set { gender = value; } }
        public char[] Grades    { get { return grades; } set { grades = value; } }


        // Constructor
        public Student()
        {
            name = "Ben Nixon";
            age = 20;
            gender = "Man";
            gradeAtRisk = false;
            grades = new char[] { 'A','B','C' }; 
        }


        // Constructor - Input
        public Student(string _name, int _age, string _gender, bool _gradeAtRisk, char[] _grades)
        {
            name = _name;
            age = _age;
            gender = _gender;
            grades = _grades;
        }
    }


A new class that hold grades of its own?
Would this be a start of a preferred solution?

    class Grades
    {
        private string label;
        private char grade;

        public string Label { get { return label; } set { label = value; } }
        public char Grade { get { return grade; } set { grade = value; } }

        // Constructor
        public Grades()
        {
            label = "Math";
            grade = 'A';
        }
    }


Or just a a method within the Students class?
        class Student
        {
            public void AddGrade()
            {
                // Do something here
            }
        }

This post has been edited by capsyl: 11 July 2020 - 05:53 AM


Is This A Good Question/Topic? 0
  • +

Replies To: Need advice on how to handle grades for a student

#2 modi123_1   User is online

  • Suitor #2
  • member icon



Reputation: 15804
  • View blog
  • Posts: 63,308
  • Joined: 12-June 08

Re: Need advice on how to handle grades for a student

Posted 11 July 2020 - 08:35 AM

Yep, a class for grades holding either a class id or a class name. If you go the latter route then you have options of using a free form string, or you use an ENUM for predefined names.

https://docs.microso...ltin-types/enum
Was This Post Helpful? 0
  • +
  • -

#3 Skydiver   User is online

  • Code herder
  • member icon

Reputation: 7462
  • View blog
  • Posts: 25,113
  • Joined: 05-May 12

Re: Need advice on how to handle grades for a student

Posted 11 July 2020 - 12:51 PM

Personally I prefer using the class to hold the grade, and then having a list of those:
class Grade
{
    public string Subject { get; }
    public char LetterGrade;
}

class Student
{
    public string Name { get; }
    public IList<Grade> Grades { get; } = new List<Grade>();
}

:

var student = new Student() { Name = "Albert Einstein" };
student.Grades.Add(new Grade() { Subject = "Math", LetterGrade = 'F' });



but I just wanted to offer up an alternative:
class Student
{
    public string Name { get; }
    public Dictionary<string, char> Grades { get; } = new Dictionary<string, char>();
}

:

var student = new Student() { Name = "Albert Einstein" };
student.Grades["Math"] = 'F';


Was This Post Helpful? 2
  • +
  • -

#4 capsyl   User is offline

  • New D.I.C Head

Reputation: 1
  • View blog
  • Posts: 49
  • Joined: 17-June 20

Re: Need advice on how to handle grades for a student

Posted 12 July 2020 - 06:17 AM

Thanks! I have a couple of questions from your example.

1. I notice you left out set; on line 3 and 10. Shouldn't attributes always be declared and set to private?
When creating an object like this. I would have written the Class Grade like below. What's your thought on this? I mean is this a good practice, to always start from this kind of structure as a beginner?:
    class Grade
    {   
        // Attribute
        private string subject;
        private char letterGrade;

        // Properties
        public string Subject { get { return subject; } set { subject = value; } }
        public char LetterGrade { get { return letterGrade; } set { letterGrade = value; } }
        }
    }



2. What does the semicolon on line 13 mean?

3. Instead of using list I'm trying to translate your example to an array. Which doesn't go quite well. I get:
'Inconsistent accessibility: property type 'Student.Grade[]' is less accessible than property 'Student.Grades'

Any ideas?

    class Student
    {
        // Attributes
        private string name;

        // Properties
        public string Name { get { return name; } set { name = value; } }
        public Grade[] Grades { get; } = new Grade[10];
    }

Was This Post Helpful? 0
  • +
  • -

#5 capsyl   User is offline

  • New D.I.C Head

Reputation: 1
  • View blog
  • Posts: 49
  • Joined: 17-June 20

Re: Need advice on how to handle grades for a student

Posted 12 July 2020 - 06:42 AM

I fixed #3 by adding public to the class likeso:
public class Grade

Was This Post Helpful? 1
  • +
  • -

#6 Skydiver   User is online

  • Code herder
  • member icon

Reputation: 7462
  • View blog
  • Posts: 25,113
  • Joined: 05-May 12

Re: Need advice on how to handle grades for a student

Posted 12 July 2020 - 08:04 AM

That's what I get when I try to write code on my phone. :)

View Postcapsyl, on 12 July 2020 - 09:17 AM, said:

1. I notice you left out set; on line 3 and 10.


Yeah, going to need that set to be able to set stuff when using the properties instead of the constructor. I'm trying to go more and more towards immutable classes lately, and hence out of habit, I only expose get's.

View Postcapsyl, on 12 July 2020 - 09:17 AM, said:

Shouldn't attributes always be declared and set to private?
When creating an object like this. I would have written the Class Grade like below. What's your thought on this? I mean is this a good practice, to always start from this kind of structure as a beginner?:
Spoiler


Depends which school of object oriented programming you come from. :)

Personally, the less code that I have to look at the better, specially if it is boilerplate code. C# 2.0 (?) introduced automatic properties so that the private attributes backing the properties are supplied by the compiler so that most people don't have to write boilerplate code.

In general, if my properties need to do any kind special validation or processing (which is rarely), then I'll use a backing property. Otherwise, I'll use automatic properties.

View Postcapsyl, on 12 July 2020 - 09:17 AM, said:

2. What does the semicolon on line 13 mean?


That was supposed to signify "there is some other code here". The code I posted above was meant to be illustrative rather than something that could just be copied and pasted.
Was This Post Helpful? 1
  • +
  • -

#7 Sheepings   User is offline

  • D.I.C Lover
  • member icon

Reputation: 241
  • View blog
  • Posts: 1,303
  • Joined: 05-December 13

Re: Need advice on how to handle grades for a student

Posted 12 July 2020 - 08:41 AM

Hers a quick revision. A little different to how Skydiver did it, none the less, I assume he will approve of the transient use of Tuples and Enums.
First start off with a Student class with a Student Constructor :
        public class Student
        {
            public Student(string student_Name, int student_Age, Gender.StudentGender student_Gender, Tuple<Grades.Subject, Grades.Grade> student_Grades)
            {
                Student_Name = student_Name;
                Student_Age = student_Age;
                Student_Gender = student_Gender;
                Student_Grades = student_Grades;
            }

            public string Student_Name { get; set; }
            public int Student_Age { get; set; }
            public Gender.StudentGender Student_Gender { get; internal set; }
            public Tuple<Grades.Subject, Grades.Grade> Student_Grades { get; internal set; }
        }

Then add a static Gender class with enums as the types :
        public static class Gender
        {
            internal protected enum StudentGender
            {
                Male, Female
            }
        }

Next I'd create a Grades class and add the grade and the subjects as type enums to the same class :
        public static class Grades
        {
            internal protected enum Subject
            {
                History, Maths, Science
            }
            internal protected enum Grade
            {
                A, B, C, D, E, F
            }
        }


Then to create a new student, you simply instantiate a new instance of student with the student details :
            Student student = new Student("Tom", 16, Gender.StudentGender.Male, Tuple.Create(Grades.Subject.Maths, Grades.Grade.B ));

Now you have one student do something with that student, ie add him to your DataGrid etc.

Here is a test version run on console app :
        internal static void Main()
        {
            Student student = new Student("Tom", 16, Gender.StudentGender.Male, Tuple.Create(Grades.Subject.Maths, Grades.Grade.B ) );

            Console.WriteLine($"Name : {student.Student_Name}, Age : {student.Student_Age}, Gender : {student.Student_Gender}, " +
                $"Graded : {student.Student_Grades.Item2}, In {student.Student_Grades.Item1}");
        }
        public class Student
        {
            public Student(string student_Name, int student_Age, Gender.StudentGender student_Gender, Tuple<Grades.Subject, Grades.Grade> student_Grades)
            {
                Student_Name = student_Name;
                Student_Age = student_Age;
                Student_Gender = student_Gender;
                Student_Grades = student_Grades;
            }

            public string Student_Name { get; set; }
            public int Student_Age { get; set; }
            public Gender.StudentGender Student_Gender { get; internal set; }
            public Tuple<Grades.Subject, Grades.Grade> Student_Grades { get; internal set; }
        }
        public static class Gender
        {
            internal protected enum StudentGender
            {
                Male, Female
            }
        }
        public static class Grades
        {
            internal protected enum Subject
            {
                History, Maths, Science
            }
            internal protected enum Grade
            {
                A, B, C, D, E, F
            }
        }

Output :
Name : Tom, Age : 16, Gender : Male, Graded : B, In Maths

This post has been edited by Sheepings: 12 July 2020 - 08:42 AM

Was This Post Helpful? 2
  • +
  • -

#8 Sheepings   User is offline

  • D.I.C Lover
  • member icon

Reputation: 241
  • View blog
  • Posts: 1,303
  • Joined: 05-December 13

Re: Need advice on how to handle grades for a student

Posted 12 July 2020 - 11:38 AM

Ok, Skydiver just pointed out that you want to have multiple grades and multiple subjects. So the above code will only work for singular result.

However, the below adaption is what I would have done. But you may find it much more complicated to follow depending on your experience.

The new class would look like this :

        public class Student
        {
            public Student(string student_Name, int student_Age, Gender.StudentGender student_Gender, Tuple<List<Grades.Subject>, List<Grades.Grade>> student_Grades)
            {
                Student_Name = student_Name;
                Student_Age = student_Age;
                Student_Gender = student_Gender;
                Student_Grades = student_Grades;
            }

            public string Student_Name { get; set; }
            public int Student_Age { get; set; }
            public Gender.StudentGender Student_Gender { get; internal set; }
            public Tuple<List<Grades.Subject>, List<Grades.Grade>> Student_Grades { get; internal set; }
        }
        public static class Gender
        {
            internal protected enum StudentGender
            {
                Male, Female
            }
        }
        public static class Grades
        {
            internal protected enum Subject
            {
                History, Maths, Science
            }
            internal protected enum Grade
            {
                A, B, C, D, E, F
            }
        }


Then to use it, you would do :
            Student student = new Student("Mary", 14, Gender.StudentGender.Male, new Tuple<List<Grades.Subject>, List<Grades.Grade>>(new List<Grades.Subject>(new List<Grades.Subject>
            { Grades.Subject.History, Grades.Subject.Science }), new List<Grades.Grade>(new List<Grades.Grade> { Grades.Grade.B, Grades.Grade.E })));


I am sure, if you spent some time, you could likely simplify it a bit more. Hope it helps divert you in the direction you need to go.

This post has been edited by Sheepings: 12 July 2020 - 11:39 AM

Was This Post Helpful? 0
  • +
  • -

#9 Skydiver   User is online

  • Code herder
  • member icon

Reputation: 7462
  • View blog
  • Posts: 25,113
  • Joined: 05-May 12

Re: Need advice on how to handle grades for a student

Posted 12 July 2020 - 04:42 PM

If you like the tuples, I highly suggest going with a list of Tuples instead of the tuple of lists. With the former you keep associated data together. With the latter, you are back to your original situation of having to maintain parallel arrays. In procedural programming languages which only supported arrays of simple types, parallel arrays offered the best and easiest way of doing things. With modern object oriented languages, as well as languages that support arrays of complex types, the usual recommendations are to avoid parallel arrays and prefer keeping data together.
Was This Post Helpful? 0
  • +
  • -

#10 Sheepings   User is offline

  • D.I.C Lover
  • member icon

Reputation: 241
  • View blog
  • Posts: 1,303
  • Joined: 05-December 13

Re: Need advice on how to handle grades for a student

Posted 12 July 2020 - 05:38 PM

The change is almost one in the same. However, OP will need to use : List<Tuple<Grades.Subject, Grades.Grade>> if they take your advice...

I'd opt for how i done it. Changing it to how you recommend makes it harder to work with the tuples. I'd rather work with a list and utilise foreach(Action, Type) on the list than deal with a possible n-tuple.

That's just me though. :smile2:
Was This Post Helpful? 0
  • +
  • -

#11 Sheepings   User is offline

  • D.I.C Lover
  • member icon

Reputation: 241
  • View blog
  • Posts: 1,303
  • Joined: 05-December 13

Re: Need advice on how to handle grades for a student

Posted 12 July 2020 - 06:07 PM

This will give you something to play with Skydiver.

Your suggestions leave the constructor limited since now you need to know how many subjects where taken and what the grades where.

        internal static void Main()
        {
            Student student = new Student("Mark", 15, Gender.StudentGender.Male, new List<Tuple<Grades.Subject, Grades.Grade, Grades.Subject, Grades.Grade>> { Tuple.Create(Grades.Subject.History, Grades.Grade.B, Grades.Subject.Science, Grades.Grade.D) });
        }
        public class Student
        {
            public Student(string student_Name, int student_Age, Gender.StudentGender student_Gender, List<Tuple<Grades.Subject, Grades.Grade, Grades.Subject, Grades.Grade>> student_Grades)
            {
                Student_Name = student_Name;
                Student_Age = student_Age;
                Student_Gender = student_Gender;
                Student_Grades = student_Grades;
            }

            public string Student_Name { get; set; }
            public int Student_Age { get; set; }
            public Gender.StudentGender Student_Gender { get; internal set; }
            public List<Tuple<Grades.Subject, Grades.Grade, Grades.Subject, Grades.Grade>> Student_Grades { get; internal set; }
        }
        public static class Gender
        {
            internal protected enum StudentGender
            {
                Male, Female
            }
        }
        public static class Grades
        {
            internal protected enum Subject
            {
                History, Maths, Science
            }
            internal protected enum Grade
            {
                A, B, C, D, E, F
            }
        }



Alterations are required to make this work, and I'm not prepared to do it, nor do I have time. I'd still prefer post #8.
Was This Post Helpful? 0
  • +
  • -

#12 Skydiver   User is online

  • Code herder
  • member icon

Reputation: 7462
  • View blog
  • Posts: 25,113
  • Joined: 05-May 12

Re: Need advice on how to handle grades for a student

Posted 12 July 2020 - 06:48 PM

I quite literally meant a list of tuples:
List<Tuple<string, char>> Student_Grades;



as opposed to a tuple of lists:
Tuple<List<string>, List<char>> Student_Grades.



I was not talking about an n-tuple like:
Tuple<string, char, string, char> Student_Grades;


because as you said, then you would have to know ahead of time the number of subjects ahead of time.
Was This Post Helpful? 0
  • +
  • -

#13 Skydiver   User is online

  • Code herder
  • member icon

Reputation: 7462
  • View blog
  • Posts: 25,113
  • Joined: 05-May 12

Re: Need advice on how to handle grades for a student

Posted 12 July 2020 - 07:49 PM

Here is where parallel arrays (or this case parallel lists) have their pitfalls:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;

public enum Gender
{
    Male, Female
}

public enum Subject
{
    History, Maths, Science, Art
}

public enum Grade
{
    A, B, C, D, E, F
}

namespace ListOfTuples
{
    public class Student
    {
        public Student(string name,
                       int age,
                       Gender gender,
                       IEnumerable<(Subject, Grade)> grades)
        {
            Name = name;
            Age = age;
            Gender = gender;
            Grades = grades.ToList();
        }

        public string Name { get; }
        public int Age { get; }
        public Gender Gender { get; }
        public IReadOnlyList<(Subject subject, Grade grade)> Grades { get; }
    }

    public static class ClassList
    {
        public static IEnumerable<Student> Create()
        {
            return new List<Student>()
            {
                new Student("Mark", 15, Gender.Male,
                            new [] {
                                (Subject.History, Grade.B ),
                                (Subject.Science, Grade.D),
                            }),
                new Student("Fred", 16, Gender.Female,
                            new [] {
                                (Subject.Maths, Grade.A),
                                (Subject.Science, Grade.B ),
                                (Subject.History, Grade.B ),
                            }),
                new Student("Susan", 14, Gender.Female,
                            new [] {
                                (Subject.History, Grade.A),
                                (Subject.Art, Grade.A),
                            }),
            };
        }
    }
}

namespace TupleOfLists
{
    public class Student
    {
        public Student(string name,
                       int age,
                       Gender gender,
                       (List<Subject>, List<Grade>) grades)
        {
            Name = name;
            Age = age;
            Gender = gender;
            Grades = grades;
        }

        public string Name { get; }
        public int Age { get; }
        public Gender Gender { get; }
        public (List<Subject> subjects, List<Grade> grades) Grades { get; }
    }

    public static class ClassList
    {
        public static IEnumerable<Student> Create()
        {
            return new List<Student>()
            {
                new Student("Mark", 15, Gender.Male,
                            (new List<Subject>() { Subject.History, Subject.Science },
                             new List<Grade>() {Grade.B, Grade.D })),
                new Student("Fred", 16, Gender.Female,
                            (new List<Subject>() { Subject.Maths, Subject.Science, Subject.History},
                             new List<Grade>() { Grade.A, Grade.B })),              // ! OOPS Forgot to give a grade
                new Student("Susan", 14, Gender.Female,
                            (new List<Subject>() { Subject.History, Subject.Art },
                             new List<Grade>() { Grade.A, Grade.A, Grade.A })),     // OOPS! Too many grades
            };
        }
    }
}


class TestConsole
{
    static void Main()
    {
        var listOfTuples = ListOfTuples.ClassList.Create();
        var tupleOfLists = TupleOfLists.ClassList.Create();
    }
}




See lines 102 and 105 where the lists were not kept in sync. It is easy to spot in this case because the number of subjects is limited to 4, but what happens when you span a students career through 4 years of schooling where they have taken multiple subjects where they would have taken 10-14 classes a year.

As a quick aside, I'm using the C# 7.0 value tuples above. So instead of writing:
Tuple<Subject, Grade>
Tuple<List<Subject>, List<Grade>>


I was using:
(Subject, Grade)
(List<Subject>, List<Grade>)



There are other differences between the classic .NET Framework 4.x Tuples and the newer C# 7.0 tuple types found here.


But any which way +1 to Sheepings for bring up another way of storing the subjects and grades with the student. Instead of declaring a new class to hold the subject and grade, let the framework/language take care of making class to via a tuple; or let a tuple hold the parallel lists of subjects and grades.
Was This Post Helpful? 0
  • +
  • -

#14 Skydiver   User is online

  • Code herder
  • member icon

Reputation: 7462
  • View blog
  • Posts: 25,113
  • Joined: 05-May 12

Re: Need advice on how to handle grades for a student

Posted 12 July 2020 - 08:02 PM

I would also be remiss to mention one of the Javascript approaches to keeping data like this together: using a JSON string.
"[ { "History" : "B" }, { "Science", "D" } ]"



Personally, I dislike "stringly-typed" data use in a strongly typed language like C#, but it is another option. When you go stringly-typed, then you completely forego any kind of help that the compiler or the debugger could offer you.
Was This Post Helpful? 0
  • +
  • -

#15 capsyl   User is offline

  • New D.I.C Head

Reputation: 1
  • View blog
  • Posts: 49
  • Joined: 17-June 20

Re: Need advice on how to handle grades for a student

Posted 13 July 2020 - 03:13 AM

Thanks a lot for your suggestions! I'll try them out! At the moment I'm playing around with Skydivers version. And have a question.

Instead of setting the size of the array for Grades in the properties:
class Student
    {
        public Grade[] Grades { get; set; } = new Grade[10] ;
    }


Can this be done somewhere in the creation of the student object?
students = new Student[5]

This post has been edited by capsyl: 13 July 2020 - 03:14 AM

Was This Post Helpful? 0
  • +
  • -

  • (3 Pages)
  • +
  • 1
  • 2
  • 3