Student Grades Using Ruby

A lot of people new to the programming game usually run across the grade book style of application at some point. The reason it is assigned as a beginner level project is that it is an easy way to show you how to use objects, how to use an array of objects, or more precisely how to use a collection. We here at DIC have seen variations of this project from Java to C# to VB.NET and beyond. Besides having trouble with creating the actual student object, those new to programming tend to get tripped up on the averages and figuring out the letter grade. So I figured “Heck, why not do a little demo with our friend Ruby?” This will show you how to create a standard object in Ruby, how we can go about getting an average and assigning the proper letter grade based on a scale. The knowledge here can be applied throughout the programming world. Lets give it a go on this episode of the Programming Underground!

The example below is very basic and something I whipped up in a few minutes of goofing around. There are three main things I want to talk to you about that you can take with you into Ruby or, more importantly, into other languages like Java or .NET. The first one is the idea of identifying an object and fleshing it out. The second is creating a method for finding an average. The third is using something we have calculated to calculate something else (like a letter grade).

Identifying a class in a problem and then “encapsulating” what kind of functionality it has is crucial to object oriented programming. When we are given a word problem in math our first task is to identify the relevant information in it. We don’t always need all the information, but we do have to identify what is important.

In a student grade book style application one object we can automatically see is “student”. How do we know student is going to be an object? Well because it is a noun. It is a person. Since it is something concrete it will have attributes like height, weight, eye color etc. Things we can model. Another less noticeable object might be a class of students. A class in school is a thing, it just happens to be made up of other things. What can a class of students do? It could have zero to many students, we could count them we could sort them based on their height we can take the class on a field trip to their local porn shop. The teacher could get furry handcuffs and …. well, you get the idea.

While our application only implements the student object as a class and then uses an array to hold our students, we could have made another object for classOfStudents that will manage numerous student objects inside it. You will often see this referred to as a “wrapper class” because it wraps around other objects… in our case a collection of students.

So whenever you are given a problem to solve with a program, sit for a minute and identify everything that would be an object and how those objects might be related to one another. Some objects will be easy to identify and some might be a little more elusive. But if you identify the main objects, sometimes it is not a huge problem to identify the other objects along the way and build them into the design.

Lets start by showing you the example code and then going through it…

class Student
	attr_accessor :firstname, :lastname
	attr_accessor :lgrade, :agrade
	
	
	# Class initializer takes the students first and last name
	def initialize(fname, lname)  
		@firstname = fname  
		@lastname = lname  
		@grades = Array.new
	end 

	# Calculates the students average grade based on all scores
	def calcAverage
		sum = 0

		# Make sure we don't have a denominator of 0, we sum up all 
		# grades and divide by number of grades to get average
		if @grades.size > 0
			@grades.each { |g| sum += g } 
			@agrade = sum / @grades.size
		else
			@agrade = 0
		end
		
		# Now that we populated @agrade, we find the letter grade and put it in @lgrade
		setLetterGrade
	end

	# Add the grade to the grades array of integers
	def addGrade(score)
		# If score is an integer or float, put it in the grades array and calculate the average
		if score.is_a?(Integer) || score.is_a?(Float)
			@grades.push(score)
			calcAverage
		end
	end
	
	# Translates average score into a letter grade equivalent
	def setLetterGrade
		if @agrade >= 90.0
			@lgrade = "A"
		elsif @agrade >= 80.0
			@lgrade = "B"
		elsif @agrade >= 70.0
			@lgrade = "C"
		elsif @agrade >= 60.0
			@lgrade = "D"
		else
			@lgrade = "F"
		end
	
	end
end

# Find the average of the entire class of students. (Average of averages)
def findClassAverage(studentsArray)
	classSum = 0.0
	studentsArray.each { |student| classSum += student.agrade }
	if studentsArray.size > 0 
		return classSum / studentsArray.size
	end
	
	return 0
end 

# Create student 1 named "John Smith" then add some grades
student1 = Student.new("John","Smith")
student1.addGrade(80)
student1.addGrade(100.0)
student1.addGrade(54.0)

# Create student 2 named "Mary Johnson" and add her scores
student2 = Student.new("Mary","Johnson")
student2.addGrade(90.5)
student2.addGrade(90)
student2.addGrade(88.3)

# Create an array of students and place both John and Mary into the array
classOfStudents = Array.new
classOfStudents[0] = student1
classOfStudents[1] = student2

# For each student in the class, output their name, average and letter grade
classOfStudents.each { |student| puts "Student name: " + student.firstname + " " + student.lastname + " and their average was " + student.agrade.to_s + " " + student.lgrade }

# Lastly print out the class percentage
puts "Class Average: " + findClassAverage(classOfStudents).to_s

Our code above has the student object defined as a class. Each student in our example has a few attributes including a first and last name as well as an average grade which is a percentage (float) and a letter grade which will reflect their grade based on the percentage. When we calculate the average we will then assign their grade at the same time by calling setLetterGrade().

This class contains an array called @grades. This array is basically a list of all the grades the student got and is added to through the use of “addGrade”. Each time we pass the score to the student the score is pushed onto this array. We also take this opportunity to then recalculate the average. This keeps a valid percentage (and the letter grade) accurate at all times.

You will notice that in our addGrade method we push on the score and call calcAverage to figure out the average. In the calcAverage we then add up all the grades in the array using an each loop (this is the same as a foreach/for loop in .NET or Java) and divide by the number of scores to create a rough average. We then put this average into our class variable @agrade. Notice I take extra caution to make sure that our array is not empty so that we don’t get into the situation of dividing by zero. calcAverage then calls setLetterGrade to run our @agrade through a series of if else tests to see if it meets a certain percentage. Then we assign the proper letter grade to @lgrade based on that.

I noticed that most newbie programmers have real problems with this if else setup to find a letter grade based on a percentage. Keep in mind that this style of if else can be almost directly applied to other languages with few syntax changes. Consider it a present from me to youuuuu. :wub:

So after we have created our student class, and have what a student does and can do, we need to create a group of them. Here I have created two student class instances and named one “John Smith” and the other “Mary Johnson”. I have assigned them some grades using our addGrade method. Great, now we have two students for our class of students! We could have, at this point, created another class to house our students. However, I opted for a shorter way of just creating an array of students called “classOfStudents” and I put both John and Mary into it.

I then created a stand alone function called “findClassAverage” which takes an array and sums up all the average scores of the students in the array. It then divides by the number of students (again taking into account a size of zero) and outputting a class average. In addition this function is very generic so it would be great to save and use in other projects. Always be on the lookout for little tidbits like this for later reuse.

At the end we loop through each student in the array (again using a foreach style method) and then the class average.

Keep in mind that most student gradebook applications go into a bit more depth than this, but you can easily take what you learned here to apply it to your application. Pay close attention to how we setup a student class, how we calculated their average, how we setup a letter grade to correspond to that average. Also note how we organized the students into a structure which we could easily loop through, count, delete etc. In essence what we did with that array is create a “collection of students”. It is an array of student objects.

I hope you learned something about the structure of the program overall. I used Ruby to illustrate some of the key points but the thing here is the structure. Remember to identify the objects, model them out by asking what kind of attributes it has, how it relates to other objects you have identified and then build accordingly.

Thanks for reading! 🙂

About The Author

Martyr2 is the founder of the Coders Lexicon and author of the new ebooks "The Programmers Idea Book" and "Diagnosing the Problem" . He has been a programmer for over 25 years. He works for a hot application development company in Vancouver Canada which service some of the biggest tech companies in the world. He has won numerous awards for his mentoring in software development and contributes regularly to several communities around the web. He is an expert in numerous languages including .NET, PHP, C/C++, Java and more.