How do I implement a search class in Ruby?
I have a list of objects of immutable values. The lookup class provides ways to iterate and query this data:
class Banker
Bank = Struct.new(:name, :bic, :codes)
attr_reader :banks
def initialize
@banks = [
Bank.new('Citibank', '1234567', ['1', '2']),
Bank.new('Wells Fargo', '7654321', ['4']), # etc.
]
end
def find_by_bic(bic)
banks.each do |bank|
return bank if bank.bic == bic
end
end
end
@banks
initialized every time it is used Banker
. What options are there for caching @banks
so that it can be reused across different instances Banker
?
source to share
I don't think I'm Struct
buying you anything here. How about this?
code
class Banker
@all_banks = {}
class << self
attr_reader :all_banks
end
attr_reader :banks
def initialize(banks)
@banks = banks.keys
banks.each { |k,v| self.class.all_banks[k] = v }
end
def find_by_bic(bic)
return nil unless @banks.include?(bic)
self.class.all_banks[bic]
end
end
Note self
c is self.class
needed to distinguish class self
from keyword class
.
Example
b1 = Banker.new({ '1234567' => { name: 'Citibank', codes: ["1", "2"] },
'7654321' => { name: 'Wells Fargo', codes: ['4'] } })
b1.banks
#=> ["1234567", "7654321"]
Banker.all_banks
#=> {"1234567"=>{:name=>"Citibank", :codes=>["1", "2"]},
# "7654321"=>{:name=>"Wells Fargo", :codes=>["4"]}}
b1.find_by_bic '7654321'
#=> {:name=>"Wells Fargo", :codes=>["4"]}
b1.find_by_bic '1234567'
#=> {:name=>"Citibank", :codes=>["1", "2"]}
b1.find_by_bic '0000000'
#=> nil
b2 = Banker.new({ '6523155' => { name: 'Bank of America', codes: ["3"] },
'1234567' => { name: 'Citibank', codes: ["1", "2"] } })
b2.banks
#=> ["6523155", "1234567"]
Banker.all_banks
#=> {"1234567"=>{:name=>"Citibank", :codes=>["1", "2"]},
# "7654321"=>{:name=>"Wells Fargo", :codes=>["4"]},
# "6523155"=>{:name=>"Bank of America", :codes=>["3"]}}
b2.find_by_bic '6523155'
#=> {:name=>"Bank of America", :codes=>["3"]}
b2.find_by_bic '1234567'
#=> {:name=>"Citibank", :codes=>["1", "2"]}
b2.find_by_bic '7654321'
#=> nil
Alternatives
If you prefer, you can add a class method instead:
def self.new(banks)
banks.each { |k,v| all_banks[k] = v }
super
end
and remove the first line in initialize
.
Or, if you have a complete list of all banks, you can simply make a all_banks
constant instead :
ALL_BANKS = {"1234567"=>{:name=>"Citibank", :codes=>["1", "2"]},
"7654321"=>{:name=>"Wells Fargo", :codes=>["4"]},
"6523155"=>{:name=>"Bank of America", :codes=>["3"]}}
def find_by_bic(bic)
return nil unless @banks.include?(bic)
ALL_BANKS[bic]
end
and change initialize
to:
def initialize(bics)
@banks = bics
end
where bics
is an array of values bic
.
source to share