I want to load a file, split its content into arrays, and have the class apply to the content.
class Student
def initialize( name, grade )
#name = name
#grade = grade
#grade = #grade.to_i
#newgrade = #grade*1.45
end
def show()
return "#{#name} ,#{#grade} , #{#newgrade}"
end
end
# Opening the file into an array
arr = File.open("exam_results.txt", "r+")
allStudents = Array.new
for a in arr
b = a.split(",")
name = b[0]
score = b[1]
allStudents << Student.new(#name, #grade)
end
for i in Student
puts show()
end
I'm getting
undefined method 'each' for Student:Class (NoMethodError)
on line 28, which is the puts show() line. Any clues on how I can get further on this?
I think you have a typo there (among other things). You're doing this:
for i in Student
puts show()
end
Clearly, the Student class is not a collection which you can iterate. I think, what you meant to write is this:
allStudents.each do |student|
puts student.show
end
That is because you are trying to iterate over "Student" class and not Array/Collection object at for i in Student
Basically you are doing it wrong. It rather should be something like
allStudents.each do |student|
puts student.show
end
Related
Okay, this is a little hard to explain but I will try (For starters I am only just learning to code so it may be something super simple I'm missing..)
I created a few classes, I put a name in those classes, I put them in an array, I then chose one at random and try to puts the name, and it outputs blank.
Am I doing this all completely wrong? I've been learning ruby for about 3 months now so I'm sure there is a lot I don't know.
class A
attr :name
def set_name
#name = "Aaa"
end
def get_name
return #name
end
end
class B
attr :name
def set_name
#name = "Bbb"
end
def get_name
return #name
end
end
class C
attr :name
def set_name
#name = "Ccc"
end
def get_name
return #name
end
end
name_a = A.new
name_b = B.new
name_c = C.new
which_name = Array.new
which_name[0] = name_a
which_name[1] = name_b
which_name[2] = name_c
roll = rand(max 3)
puts which_name[roll].get_name
I then chose one at random and try to puts the name, and it outputs
blank.
You never called the #set_name method in your code. You can add this:
name_a.set_name
name_b.set_name
name_c.set_name
Also, you probably want to look into #attr_accessor.
I need to randomly pick a name from an array in Ruby and then check if it uppercase. So far I have:
def namegenerator
return #name.sample
end
def namechecker
if name.upcase then
check = TRUE
else
check = FALSE
end
end
It needs to be as two separate methods like this.
Something like this:
def sample_word(words)
words.sample
end
def upcase?(word)
word == word.upcase
end
And then something like:
words = %w[APPLE banana CherRy GRAPE]
word = sample_word(words)
puts word # e.g. BANANA
puts upcase?(word) # will print true
If you just want to check just the first letter:
names = %w(Kirk mccoy scott Spock)
names.sample.then { |name| [name, name[0] == name[0].upcase] }
#=> ["mccoy", false]
Maybe something like this:
class NameGenerator
def initialize(size, items)
#name = ""
#size = size
#items = items
end
def namegenerator
#name = #items.sample(#size).to_s
end
def namechecker?
#name == #name.upcase
end
def name
#name
end
end
ng = NameGenerator.new 1, ["name", "Name", "NAME"]
ng.namegenerator
puts ng.name, ng.namechecker?
Update
I've posted code without much thinking about abstraction and i think it would be much better to encapsulate name and upper case check to separate class and make it immutable, then make generator class that selects one entity from collection.
class NameGenerator
def initialize(items)
#items = items
end
def next
#items.sample
end
end
class Name
attr_reader :name
def initialize(name)
#name = name
end
def is_uppercase?
#name.match(/\p{Lower}/) == nil
end
end
ng = NameGenerator.new [
Name.new("name"),
Name.new("Name"),
Name.new("NAME"),
Name.new("na-me")
]
name = ng.next
puts name.name, name.is_uppercase?
I'm trying to access a class variable from a method outside of the class.
This is my class:
class Book
##bookCount = 0
##allBooks = []
def self.allBooks
##allBooks
end
def self.bookCount
##bookCount
end
attr_accessor :name,:author,:date,:genre,:rating
def initialize(name, author, date, genre, rating)
#name = name
#author = author
#date = date
#genre = genre
#rating = rating
##bookCount += 1
##allBooks << self
end
end
This is the method trying to access the class variable ##bookCount
def seeBookShelf
if ##bookCount == 0
puts "Your bookshelf is empty."
else
puts "You have " + #bookCount + " books in your bookshelf:"
puts allBooks
end
end
When I try to execute the method I get this:
undefined local variable or method `bookCount' for main:Object (NameError)
How can I access bookCount from the outside?
Use class_variable_get to access a class variable outside of a class:
class Foo
##a = 1
end
Foo.class_variable_get(:##a)
=> 1
For most cases, class instance variables are preferred to class variables. The latter are prone to all manner of strange behaviour when used with inheritance.
Consider:
class Book
#book_count = 0
#all_books = []
class << self
attr_reader :book_count
attr_reader :all_books
end
# further code omitted.
end
With this code Book.book_count and Book.all_books get the expected data.
You can use class_eval to evaluate a block of code within the scope of a specific class:
class Book
##bookCount = 1
end
Book.class_eval '##bookCount'
# => 1
And just for fun... you can actually do all kinds of trickery with class_eval such as define a new method in the class without monkey patching:
Book.class_eval { ##bookCount = 5 }
Book.class_eval '##bookCount'
# => 5
Book.class_eval do
def self.hey_look_a_new_method
return "wow"
end
end
Book.hey_look_a_new_method
# => "wow"
You need a getter to access the class variable, try this code.
See http://www.railstips.org/blog/archives/2006/11/18/class-and-instance-variables-in-ruby/ for an explanation.
You are also better to use string interpolation otherwise you get a Type error, also it is more Rubyesque.
class Book
##bookCount = 0
def self.bookCount
##bookCount
end
end
def seeBookShelf
if Book.bookCount == 0
puts "Your bookshelf is empty."
else
puts "You have #{Book.bookCount} books in your bookshelf:"
end
end
seeBookShelf # Your bookshelf is empty.
You have to specify the Class of the variable :
def seeBookShelf
if Book.bookCount == 0
puts "Your bookshelf is empty."
else
puts "You have " + Book.bookCount + " books in your bookshelf:"
puts Book.allBooks
end
end
I want to put the name of the enemies (toto and titi). How can I do that ?
My simplified code :
class Gamer
attr_accessor :name, ...
def initialize(name)
#name = name
...
end
...
end
class Enemy < Gamer
...
end
class Map
attr_accessor :enemies
...
end
##############
map = Map.new
map.enemies = [
Enemy.new("toto"),
Enemy.new("titi")
]
puts "#{map.enemies}"
I'm a beginner in Ruby
return :
[#<Gamer:0x000002e29da0 #name="toto">, #<Gamer:0x000002e29d50 #name="titi">]
If I understand your question correctly then:
map.enemies.each do |enemy|
puts enemy.name
end
Return an array of names
puts map.enemies.map(&:name)
You can define the method to_s in Enemy. This method is used when you puts an object:
class Enemy
...
def to_s
#name
end
end
enemy = Enemy.new("foo")
puts enemy
#=> foo
To print the names of an Array of objects, you can then use join on the Array:
map = Map.new
map.enemies = [Enemy.new("foo"), Enemy.new("bar")]
puts map.enemies.join(", ")
#=> foo, bar
This has the benefit that the Enemy object now is responsible for knowing how it should be printed, rather than this behaviour being spread across the code that uses Enemy.
I have a csv file:
Name,Grade
John Smith,75
Sally Field,85
etc…….
That I am reading using the following class in a separate file 'test_scores.rb':
require'csv'
require_relative('./test_scores_student_class')
class TestScores
attr_accessor :filename
def initialize(filename)
#filename = filename
end
def make_score_list
score_list = []
CSV.foreach(#filename, headers:true) do |record|
name = record['Name']
grade = record['Grade'].to_i
student = Student.new(name, grade)
score_list << student
end
score_list
end
def grade_count
a = []
b = []
c = []
d = []
self.each do |student|
binding.pry
if student['Grade'] >= 90
a << student
elsif student['Grade'] >= 80 && student['Grade'] < 90
b << student
elsif student['Grade'] >= 70 && student['Grade'] < 80
c << student
else
d << student
end
end
puts ">= 90: #{a.count}"
puts ">= 80: #{b.count}"
puts ">= 70: #{c.count}"
puts " < 70: #{d.count}"
end
end
test_scores = TestScores.new('scores.csv')
grade_list = test_scores.make_score_list
grade_list.grade_count
When I attempt to call the method grade_count on the grade_list array I get the undefined method error. How can I re-write the method so that it is callable on grade_list while still preserving the functionality it is designed for?
Your grade_list is an Array and not an instance of your TestScores class. Therefore grade_list does not have a method called grade_count.
To fix that you can either make grade_count a class function and call it with an array parameter like TestScores.grade_count(grade_list) or you can make your TestScores class an Enumerable and make make_score_list return itself instead of an array.
Well guessing your intent.
score_list in the make_score_list method should be an instance variable
Then grade count should do #score_list.each.
Another way would be to make them both both class methods one returning an array of Student the other taking that as an argument.
The problem with your code is grade_count is a method on TestScores, not score_list.
Which would be another option, i.e creating a class that held an array of student.