繁体   English   中英

Ruby 遍历哈希数组

[英]Ruby iterate over an array of hashes

我有以下哈希数组。 我想向“所有”数组中的“哈希”添加一个新的键值对。 有没有比我目前正在做的更好的循环方式?

stack = {
  "all": [
    "mango",
    "apple",
    "banana",
    "grapes"
  ],
  "mango": {
    "TYPE": "test",
    "MAX_SIZE": 50,
    "REGION": "us-east-1"
  },
  "apple": {
    "TYPE": "dev",
    "MAX_SIZE": 55,
    "REGION": "us-east-1"
  },
  "banana": {
    "TYPE": "test",
    "MAX_SIZE": 60,
    "REGION": "us-east-1"
  },
  "grapes": {
    "TYPE": "dev",
    "MAX_SIZE": 80,
    "REGION": "us-east-1"
  },
  "types": [
    "dev",
    "test"
  ]
}

我的代码:

  stack['all'].each do |fruit|
   stack[fruit].each do |fruit_name|
     stack[fruit_name]['COUNT'] = stack[fruit_name]['MAX_SIZE'] * 2
   end
  end

预期输出:

stack = {
  "all": [
    "mango",
    "apple",
    "banana",
    "grapes"
  ],
  "mango": {
    "TYPE": "test",
    "MAX_SIZE": 50,
    "REGION": "us-east-1",
    "COUNT" : 100
  },
  "apple": {
    "TYPE": "dev",
    "MAX_SIZE": 55,
    "REGION": "us-east-1",
    "COUNT" : 110
  },
  "banana": {
    "TYPE": "test",
    "MAX_SIZE": 60,
    "REGION": "us-east-1",
    "COUNT" : 120
  },
  "grapes": {
    "TYPE": "dev",
    "MAX_SIZE": 80,
    "REGION": "us-east-1",
    "COUNT" : 160
  },
  "types": [
    "dev",
    "test"
  ]
}

不需要第二个循环。 以下是你想要的:

keys = stack[:all].map(&:to_sym)
keys.each do |key|
  stack[key][:COUNT] = stack[key][:MAX_SIZE] * 2
end

在上面的代码块stack[:all]将返回一个键数组作为字符串, .map(&:to_sym)将结果数组中的每个字符串转换为一个符号。


实现相同结果的另一种方法是使用fetch_valuesvalues_at来检索属于提供的键的值数组。 不同之处在于,如果某个键丢失,则fetch_values会引发异常,而values_at为该键返回nil

fruits = stack.fetch_values(*stack[:all].map(&:to_sym))
fruits.each do |fruit|
  fruit[:COUNT] = fruit[:MAX_SIZE] * 2
end

如果你想知道为什么在stack[:all].map(&:to_sym)之前有一个* ,这是为了将数组转换为单独的参数。 在这种情况下, *称为spat 运算符

您可以按如下方式编写代码。

stack[:all].each do |k|
  h = stack[k.to_sym]
  h[:COUNT] = 2*h[:MAX_SIZE] unless h.nil?
end

例如,当 `k = "mango" 时,

h #=> h={:TYPE=>"test", :MAX_SIZE=>50, :REGION=>"us-east-1", :COUNT=>100}

我出于三个原因定义了局部变量h

  • 它通过避免多次引用stack[k.to_sym]来简化代码
  • 在调试时,能够检查h可能会有所帮助
  • 它使代码更具可读性

请注意, h仅保存现有的哈希值; 它不会创建该哈希的副本,因此它对内存需求的影响可以忽略不计。

定义局部变量以保存属于其他对象的对象的技术对于更复杂的对象尤其有用。 例如,假设我们有散列

 hash = {
   cat: { sound: "purr", lives: 9 },
   dog: { sound: "woof", toys: ["ball", "rope"] }
 }

现在假设我们想添加一个狗玩具

new_toy = "frisbee"

如果它不存在于数组中

hash[:dog][:toys]

我们可以写

hash[:dog][:toys] << new_toy unless hash[:dog][:toys].include?(new_toy)
  #=> ["ball", "rope", "frisbee"] 
hash
  #=> {:cat=>{:sound=>"purr", :lives=>9},
  #    :dog=>{:sound=>"woof", :toys=>["ball", "rope", "frisbee"]}} 

或者,我们可以写

dog_hash = hash[:dog]
  #=> {:sound=>"woof", :toys=>["ball", "rope"]} 
dog_toys_arr = dog_hash[:toys]
  #=> ["ball", "rope"] 
dog_toys_arr << new_toy unless dog_toys_arr.include?(new_toy)  
  #=> ["ball", "rope", "frisbee"] 
hash
  #=> {:cat=>{:sound=>"purr", :lives=>9},
  #    :dog=>{:sound=>"woof", :toys=>["ball", "rope", "frisbee"]}} 
 

后一个片段不仅显示中间结果,而且在执行速度和存储要求方面可能是第一个片段的清洗,并且可以说更具可读性。 它还减少了粗心的错误,例如

hash[:dog][:toys] << new_toy unless hash[:dog][:toy].include?(new_toy)

例如,如果stack[:all]一个元素是"pineapple"stack[:pineapple] #=> nil因为stack没有键:pineapple 但是,如果stack包含键值对

nil=>{ sound: "woof", toys: ["ball", "rope"] }

那将成为问题。 牵强? 也许吧,但这也许是一种很好的做法——部分是为了可读性——避免假设h[k] #=> nil意味着h没有键k 相反,使用if h.key?(k) 例如:

stack[:all].each do |k|
  key = k.to_sym
  if stack.key?(key)
    h = stack[key]
    h[:COUNT] = 2*h[:MAX_SIZE]
  end
end

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM