[英]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_values
或values_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.