Ruby Search by using input - ruby

modify this code to create search by first name using user input output must show age and title
# A simple Employee class
class Employee
attr_reader :first_name, :last_name, :title, :age
def initialize(fname, lname, title, age)
#first_name = fname
#last_name = lname
#title = title
#age = age
end
# A string representation of the Employee object
def to_s
"#{first_name} #{last_name}, #{title}, #{age}"
end
end
# The collection class for Employee objects
class Employees
include Enumerable
def initialize
#employees = []
end
# Add Employee objects to the collection
def <<(employee)
#employees << employee
end
# Method mandated by the Enumerable module
def each
#employees.each { |e| yield(e) }
end
end
employees = Employees.new
employees << Employee.new('Anita', 'Baker', 'President', 48)
employees << Employee.new('Frank', 'Gifford', 'Director', 58)
employees << Employee.new('Barbara', 'Eden', 'Secretary', 34)
employees << Employee.new('George', 'Clooney', 'Project Manager', 37)
employees << Employee.new('Emily', 'Davies', 'Programmer', 28)
employees << Employee.new('David', 'Faber', 'Programmer', 55)
employees << Employee.new('Cindy', 'Adams', 'Programmer', 33)
employees << Employee.new('Helen', 'Hamilton', 'Business Analyst', 42)

You have already done all the necessary work by implementing Enumerable interface. Just accept user input and call the Enumerable#find method:
fname = gets.chomp
e = employees.find {|i| i.first_name == fname}

Related

how to add created objects in static array?

How can i add all created objects in self.persons ?
class Person
attr_accessor :name, :last_name, :age
def initialize(name, last_name, age = "no_age")
##persons = []
#name = name
#last_name = last_name
#age = age
add(#name, #last_name, #age)
end
def self.persons
##persons
end
private
def add(name, last_name, age)
##persons << [name, last_name, age]
end
end
person1 = Person.new("name1", "lastname1", 12)
person2 = Person.new("name2", "lastname2", 16)
p Person.persons # => [["name2", "lastname2", 16]]
You can use "conditional assignment operator" to check if array is nil.
def initialize(name, last_name, age = "no_age")
##persons ||= []
#name = name
#last_name = last_name
#age = age
add(#name, #last_name, #age)
end

Find object by attribute

I have a ruby class like this:
class Table
def initialize(name)
#name = name
#columns = {}
end
end
I'm creating different objects:
table_1 = Table.new("First")
table_2 = Table.new("Second")
table_3 = Table.new("Third")
How can I find among the objects of the Table class the object having "Second" as name attribute ?
Without creating any additional structures of data:
ObjectSpace.each_object(Table).find { |object| object.instance_variable_get(:#name) == "Second" }
=> #<Table:0x007f9f912b0ce0 #columns={}, #name="Second">
You can keep a reference to an array of instances in the class.
class Table
#instances = []
class << self
attr_accessor :instances
end
def initialize(name)
#name = name
#columns = {}
self.class.instances << self
end
end
Then you can get all the instances by
Table.instances
This, however, will prevent all the Table objects being garbage collected, so it is only feasible if you have only a small amount of Tables and that amount never grows, otherwise you'll have a memory leak.
Let's add a getter method for the name attribute
class Table
attr_reader :name
def initialize(name)
#name = name
#columns = {}
end
end
Now, if you have an array of Table objects
arr = [Table.new("First"), Table.new("Second"), Table.new("Third")]
You can find by name
arr.find { |table| table.name == "Second" }
=> #<Table:0x007f862107eb18 #name="Second", #columns={}>
You can use enumerable find or any similar method from the enumerable module:
class Table
attr_reader :name
def initialize(name)
#name = name
#columns = {}
end
end
table_1 = Table.new("First")
table_2 = Table.new("Second")
table_3 = Table.new("Third")
x = [table_1, table_2, table_3].find { |t| t.name == "Second" }
puts x.name => "Second"
Let's say we have reader on name in Table:
class Table
attr_reader :name
def initialize(name)
#name = name
#columns = {}
end
end
I'd encourage you to store these Table classes' objects in a list/array:
table_1 = Table.new("First")
table_2 = Table.new("Second")
table_3 = Table.new("Third")
tables = [table_1, table_2, table_3]
Which can then be used for finding it using find(as mentioned in one of the answers) or detect:
tables.detect { |t| t.name == "Second" } #=> table_2 object
If you'd like to go one more step ahead then we can have another class maintaining this array:
class TableList
attr_reader :tables
def initialize
#tables = tables
end
def add(table)
#tables << table
end
def find_by_name(name)
tables.detect{ |table| table.name == name }
end
end
Which can then be used as:
table_1 = Table.new("First")
table_2 = Table.new("Second")
table_3 = Table.new("Third")
table_list = TableList.new
table_list.add(table_1)
table_list.add(table_2)
table_list.add(table_3)
table_list.find_by_name('Second') #=> table_2 object

m.one + m.two + m.three doesn't work

This is my code:
class Person
def initialize(first_name, last_name, age)
#first_name = first_name
#last_name = last_name
#age = age
end
def first_name
puts #first_name
end
def last_name
puts #last_name
end
def age
puts #age
end
end
class Musician < Person
def initialize(first_name, last_name, age, instrument)
#first_name = first_name
#last_name = last_name
#age = age
#instrument = instrument
end
def instrument
puts #instrument
end
end
Then when I try to do the following:
m = Musician.new("George", "Harrison", 58, "guitar")
m.first_name + " " + m.last_name + ": " + m.age.to_s
I get an error:
in <main>': undefined method+' for nil:NilClass (NoMethodError)
Why can't I just concatenate the results of objects method?
all your methods return nil rather than the value you wish, that is, "puts" returns nil.
just eliminate the "puts" and try again

Ruby class instance and inheritance

I want to make a subclass to add attributes to the subclass in addition to the superclass. this is what I've tried:
Version I:
class Person
attr_reader :first_name, :last_name, :age
def initialize (first_name, last_name, age)
#first_name = first_name
#last_name = last_name
#age = age
end
end
class Musician < Person
attr_reader :first_name, :last_name, :age, :instrument
def initialize (first_name, last_name, age, instrument)
super
#instrument
end
end
Version II
class Person
ATTRS = ['first_name', 'last_name', 'age']
def attributes
ATTRS
end
end
class Musician < Person
ATTRS = ['instrument']
def attributes
super + ATTRS
end
end
Neither of these work.
In version 1 try
class Musician < Person
attr_reader :instrument
def initialize(first_name, last_name, age, instrument)
# Pass arguments to the super class' constructor!
super first_name, last_name, age
#instrument = instrument
end
end
Thanks to attr_reader :first_name, :last_name, :age in Person Musician will have those three accessors available because of inheritance.

In Ruby is there a way to overload the initialize constructor?

In Java you can overload constructors:
public Person(String name) {
this.name = name;
}
public Person(String firstName, String lastName) {
this(firstName + " " + lastName);
}
Is there a way in Ruby to achieve this same result: two constructors that take different arguments?
The answer is both Yes and No.
You can achieve the same result as you can in other languages using a variety of mechanisms including:
Default values for arguments
Variable Argument lists (The splat operator)
Defining your argument as a hash
The actual syntax of the language does not allow you to define a method twice, even if the arguments are different.
Considering the three options above these could be implemented with your example as follows
# As written by #Justice
class Person
def initialize(name, lastName = nil)
name = name + " " + lastName unless lastName.nil?
#name = name
end
end
class Person
def initialize(args)
name = args["name"]
name = name + " " + args["lastName"] unless args["lastName"].nil?
#name = name
end
end
class Person
def initialize(*args)
#Process args (An array)
end
end
You will encounter the second mechanism frequently within Ruby code, particularly within Rails as it offers the best of both worlds and allows for some syntactic sugar to produce pretty code, particularly not having to enclose the passed hash within braces.
This wikibooks link provides some more reading
I tend to do
class Person
def self.new_using_both_names(first_name, last_name)
self.new([first_name, last_name].join(" "))
end
def self.new_using_single_name(single_name)
self.new(single_name)
end
def initialize(name)
#name = name
end
end
But I don't know if this is the best approach.
class Person
def initialize(name, lastName = nil)
name = name + " " + lastName unless lastName.nil?
#name = name
end
end
class StatementItem
attr_reader :category, :id, :time, :amount
def initialize(item)
case item
when Order
initialize_with_order(item)
when Transaction
initialize_with_transaction(item)
end
end
def valid?
!(#category && #id && #time && #amount).nil?
end
private
def initialize_with_order(order)
return nil if order.status != 'completed'
#category = 'order'
#id = order.id
#time = order.updated_at
#amount = order.price
end
def initialize_with_transaction(transaction)
#category = transaction.category
#id = transaction.id
#time = transaction.updated_at
#amount = transaction.amount
end
end
You can use konstructor gem to declare multiple constructors in Ruby and imitate overloading:
class Person
def initialize(name)
#name = name
end
konstructor
def from_two_names(first_name, last_name)
#name = first_name + ' ' + last_name
end
end
Person.new('John Doe')
Person.from_two_names('John', 'Doe')
You could use the double splat operator ** in conjunction with logical or (double pipes) || inside the initialize method to achieve the same effect.
class Person
def initialize(**options)
#name = options[:name] || options[:first_name] << ' ' << options[:last_name]
end
end
james = Person.new(name: 'James')
#=> #<Person #name="James">
jill_masterson = Person.new(first_name: 'Jill', last_name: 'Masterson')
#=> #<Person #name="Jill Masterson">
However, if a new Person is created without a first_name, then the append << operation will fail with NoMethodError: undefined method '<<' for nil:NilClass. Here is a refactored initialize method to handle this case (using strip to remove whitespace if either option is excluded).
class Person
def initialize(**options)
#name = options[:name] || [ options[:first_name] , options[:last_name] ].join(' ').strip
end
end
goldfinger = Person.new(last_name: 'Goldfinger')
#=> #<Person #name="Goldfinger">
oddjob = Person.new(first_name: 'Oddjob')
#=> #<Person #name="Oddjob">
In fact, this approach handles calling Person.new without arguments or with an unexpected key to return the new instance with #name set to an empty string:
nameless = Person.new
#=> <#Person #name="">
middle_malcom = Person.new(middle_name: 'Malcom')
#=> <#Person #name="">
checkout functional-ruby gem which is inspired by Elixir pattern matching features.
class Person
include Functional::PatternMatching
defn(:initialize, String) { |name|
#name = name
}
defn(:initialize, String, String) {|first_name, last_name|
#name = first_name + ' ' + last_name
}
end

Resources