snippetrubyMinor
How do I compare two integer value hashes?
Viewed 0 times
valuetwohasheshowcompareinteger
Problem
I created a function that compares two hashes and returns a report on whether the subtraction of their values is negative or not.
The problem comes down to having a cost hash for a building and a current resources hash like:
This one should return a report hash like:
Now, in the cost hash,
My first attempt is definitely not the Ruby way and I don't like the function. It works, but I want to find a way to write it in a better manner:
How should I rewrite this? I don't like the
The problem comes down to having a cost hash for a building and a current resources hash like:
cost = {:wood => 300, :stone => 200, :gold => 100}
reqs = {:wood => 200, :stone => 220, :gold => 90}This one should return a report hash like:
report = { :has_resources => false, :has_wood => true, :has_stone => false, :has_gold => true }Now, in the cost hash,
:gold, :stone or :wood can be nil, i.e. non-existent. My first attempt is definitely not the Ruby way and I don't like the function. It works, but I want to find a way to write it in a better manner:
def has_resources?(cost)
report = { :has_resources => true, :has_wood => true, :has_stone => true, :has_gold => true }
if not cost[:wood].nil?
if self.wood < cost[:wood]
report[:has_wood] = false
report[:has_resources] = false
end
end
if not cost[:stone].nil?
if self.stone < cost[:stone]
report[:has_stone] = false
report[:has_resources] = false
end
end
if not cost[:gold].nil?
if self.gold < cost[:gold]
report[:has_gold] = false
report[:has_resources] = false
end
end
endHow should I rewrite this? I don't like the
.nil? checks here, but I have to include them since the < operator does not work on nil objects. I also don't like having so many ifs.Solution
Here's a version that is a bit more DRY. It also makes use of ruby's default value for hashes, and I threw in a dummy class in order to make the tests actually run.
This could probably be done even better, but one thing you really should try to achieve when writing ruby code (or any code at all, for that matter) is to try not to repeat yourself too much. That's where DRY come in.
...and here are some specs for it as well:
This could probably be done even better, but one thing you really should try to achieve when writing ruby code (or any code at all, for that matter) is to try not to repeat yourself too much. That's where DRY come in.
class CostCalculation
attr_accessor :wood, :stone, :gold
def initialize(reqs)
self.wood = reqs[:wood] || 0
self.stone = reqs[:stone] || 0
self.gold = reqs[:gold] || 0
end
def has_resources?(cost)
report = Hash.new(true)
cost.default = 0
attributes.each do |key, value|
report[:"has_#{key}"] = cost[key] <= value
end
if report.values.include?(false)
report[:has_resources] = false
end
report
end
def attributes
{ wood: self.wood, stone: self.stone, gold: self.gold }
end
end...and here are some specs for it as well:
describe CostCalculation do
it "has enough resources for something that's free" do
report = CostCalculation.new({}).has_resources?({})
report[:has_resources].should == true
end
%w[wood stone gold].each do |resource|
it "does not have enough resources if #{resource} is missing" do
cost = {resource.to_sym => 1}
report = CostCalculation.new({wood: 0, stone: 0, gold: 0}).has_resources?(cost)
report[:has_resources].should == false
report[:"has_#{resource}"].should == false
end
end
endCode Snippets
class CostCalculation
attr_accessor :wood, :stone, :gold
def initialize(reqs)
self.wood = reqs[:wood] || 0
self.stone = reqs[:stone] || 0
self.gold = reqs[:gold] || 0
end
def has_resources?(cost)
report = Hash.new(true)
cost.default = 0
attributes.each do |key, value|
report[:"has_#{key}"] = cost[key] <= value
end
if report.values.include?(false)
report[:has_resources] = false
end
report
end
def attributes
{ wood: self.wood, stone: self.stone, gold: self.gold }
end
enddescribe CostCalculation do
it "has enough resources for something that's free" do
report = CostCalculation.new({}).has_resources?({})
report[:has_resources].should == true
end
%w[wood stone gold].each do |resource|
it "does not have enough resources if #{resource} is missing" do
cost = {resource.to_sym => 1}
report = CostCalculation.new({wood: 0, stone: 0, gold: 0}).has_resources?(cost)
report[:has_resources].should == false
report[:"has_#{resource}"].should == false
end
end
endContext
StackExchange Code Review Q#7920, answer score: 6
Revisions (0)
No revisions yet.