1

这是一个简单的家庭作业......

给定一个看起来像这样的哈希:

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 不维护哈希的迭代顺序,我知道我可以使用不同的版本来解决这个问题,但我希望深入研究这一点并学习 1.9.3 之前的替代方法。我永远不知道它什么时候会派上用场。

这是一篇类似的帖子:“ Ruby 维护哈希插入顺序

4

4 回答 4

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

这是另一种看待它的方式......

Enumerablezip让我们连接两个数组的元素,将它们交织在一起:

DESIRED_ORDER.zip(COST_OF_GROCERIES.values_at(*DESIRED_ORDER)) # => [["milk", 3.5], ["egg", 1.5], ["broccolli", 0.75]]

我们可以将输出 from 传递给以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/H​​aml 的起步仅几步之遥。

我把上面的步骤分解成更小的步骤,但实际的过程可以写成:

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
于 2014-01-10T05:54:42.863 回答
0

基本上,要以任意顺序迭代哈希,最好的方法通常是用一个键数组指定顺序并循环它。因此,您将拥有一个类似 的数组['eggs', 'milk', 'broccoli'],并且您可以访问相应键的哈希对象。

于 2014-01-10T06:05:58.167 回答
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
于 2014-01-10T04:48:22.397 回答
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
于 2014-01-10T09:26:43.557 回答