这是一项简单的家庭作业......
给出一个如下所示的哈希:
cost_of_groceries = {
"milk" => 3.50,
"egg" => 1.50,
"broccolli" => 0.75
}
我想打印哪些杂货不到2美元,哪些杂货超过2美元。这将是一个示例输出:
milk is more than $2
eggs is less than $2
broccolli is less than $2
没问题,但是这不是使用Ruby 1.8.7以正确的顺序打印。
我的代码:
cost_of_groceries.each do |x,y|
if y > 2
puts "#{x} is more than $2"
else
puts "#{x} is less than $2"
end
end
这就是我得到的:
broccolli is less than $2
egg is less than $2
milk is more than $2
=> {"broccolli"=>0.75, "egg"=>1.5, "milk"=>3.5}
我意识到1.9之前的Ruby没有维护Hash上的迭代顺序,我知道我可以使用不同的版本来解决这个问题,但我希望深入研究这个并学习1.9之前的替代方法0.3。我不知道什么时候会派上用场。
这是一篇类似的帖子:“Ruby maintain Hash insertion order”
答案 0 :(得分:2)
1.9之前的Ruby没有维持哈希的“插入”顺序。这是一种强制已知订单的方法,而不依赖于排序:
BASE_COST = 2.0
COST_OF_GROCERIES = {
"milk" => 3.50,
"egg" => 1.50,
"broccolli" => 0.75
}
DESIRED_ORDER = %w[milk egg broccolli]
COST_OF_GROCERIES.values_at(*DESIRED_ORDER) # => [3.5, 1.5, 0.75]
只按所需顺序返回值。
以下是按相同顺序处理哈希的方法:
DESIRED_ORDER.each do |k|
lt_gt = COST_OF_GROCERIES[k] > BASE_COST ? 'more' : 'less'
puts '%s is %s than %0.2f' % [k, lt_gt, BASE_COST]
end
# >> milk is more than 2.00
# >> egg is less than 2.00
# >> broccolli is less than 2.00
这是另一种看待它的方式......
Enumerable的zip
让我们加入两个数组元素,交织它们:
DESIRED_ORDER.zip(COST_OF_GROCERIES.values_at(*DESIRED_ORDER)) # => [["milk", 3.5], ["egg", 1.5], ["broccolli", 0.75]]
我们可以将zip
的输出传递给map
,以添加价格是“更多”还是“更少”:
groceries = DESIRED_ORDER.zip(COST_OF_GROCERIES.values_at(*DESIRED_ORDER)).map{ |grocery, price|
[
grocery,
price,
price > BASE_COST ? 'more' : 'less'
]
}
groceries # => [["milk", 3.5, "more"], ["egg", 1.5, "less"], ["broccolli", 0.75, "less"]]
查看groceries
的内容:如果使用ERB或Haml渲染网页,则数组数组正是您要传递给视图的数据类型。
然后我们可以生成一些输出字符串并打印出来:
puts groceries.map{ |ary|
'%s, at $%.2f is %s than $%0.2f' % [*ary, BASE_COST]
}
# >> milk, at $3.50 is more than $2.00
# >> egg, at $1.50 is less than $2.00
# >> broccolli, at $0.75 is less than $2.00
使用格式字符串类似于ERB或Haml模板。这距离ERB / Haml的起步只有几步之遥。
我将上述内容分成了更小的步骤,但实际的过程可以写成:
puts DESIRED_ORDER.zip(COST_OF_GROCERIES.values_at(*DESIRED_ORDER)).map{ |grocery, price|
[
grocery,
price,
price > BASE_COST ? 'more' : 'less'
]
}.map{ |ary|
'%s, at $%.2f is %s than $%0.2f' % [*ary, BASE_COST]
}
# >> milk, at $3.50 is more than $2.00
# >> egg, at $1.50 is less than $2.00
# >> broccolli, at $0.75 is less than $2.00
答案 1 :(得分:0)
这是我能够混搭的:它有效,但违反了作业问题的背景:
hash={}
hash.instance_eval do
def []=(key,val)
ordered_keys << key
super(key,val)
end
def ordered_keys
@ordered_keys ||= []
end
def each_in_order(&block)
ordered_keys.each do |key|
yield(key, self[key])
end
end
end
hash['milk'] = 3.50
hash['egg'] = 1.50
hash['broccolli'] = 0.75
hash.each_in_order do |key, val|
if val > 2
puts "#{key} is more Than $2"
else
puts "#{key} is less than $2"
end
end
答案 2 :(得分:0)
基本上,要使用任意顺序迭代哈希,最好的方法通常是使用键数组指定顺序并循环遍历。所以你有一个像['eggs', 'milk', 'broccoli']
这样的数组,你可以访问相应键的哈希对象。
答案 3 :(得分:0)
我认为通过继承Hash
维护秩序可能是一项有趣的练习。 []=(other)
可用于添加或更改元素,delete
可用于删除它们,但我没有尝试覆盖添加或删除元素的其他哈希方法:
class MyHash < Hash
def initialize(*vals)
pairs = vals.each_slice(2).to_a
@keys = pairs.map(&:first)
pairs.each_with_object(super()) {|(k,v), h| h[k] = v}
end
def []=(key,val)
@keys << key unless @keys.include?(key)
super
end
def delete(key)
@keys.delete(key)
super
end
def print_prices(cutoff)
@keys.each do |k|
if self[k] < cutoff
puts "#{k} is less than %0.2f" % [cutoff]
else
puts "#{k} is at least %0.2f" % [cutoff]
end
end
end
end
h = MyHash.new("milk", 3.50, "egg", 1.50, "broccolli", 0.75)
# => {"milk"=>3.5, "egg"=>1.5, "broccolli"=>0.75}
h.print_prices(2.00)
milk is at least 2.00
egg is less than 2.00
broccolli is less than 2.00
h["fish"] = 2.00
h["egg"] = 2.10
h.delete("milk")
h.print_prices(2.00)
egg is at least 2.00
broccolli is less than 2.00
fish is at least 2.00