简体   繁体   English

Ruby 遍历哈希数组

[英]Ruby iterate over an array of hashes

I have the below array of hashes.我有以下哈希数组。 I want to add a new key,value pair to "hashes" which are in "all" array.我想向“所有”数组中的“哈希”添加一个新的键值对。 Is there any better way of looping through, than what I am doing currently?有没有比我目前正在做的更好的循环方式?

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"
  ]
}

My code:我的代码:

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

Expected output:预期输出:

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"
  ]
}

There is no need for the second loop.不需要第二个循环。 The following does what you want:以下是你想要的:

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

In the above code-block stack[:all] will return an array of keys as strings, .map(&:to_sym) will convert each string in the resulting array into a symbol.在上面的代码块stack[:all]将返回一个键数组作为字符串, .map(&:to_sym)将结果数组中的每个字符串转换为一个符号。


Another way to achieve the same result would be to use eitherfetch_values or values_at to retrieve an array of values belonging to the provided keys.实现相同结果的另一种方法是使用fetch_valuesvalues_at来检索属于提供的键的值数组。 The difference being that fetch_values raises an exception if a key is missing while values_at returns nil for that key.不同之处在于,如果某个键丢失,则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

If you are wondering why there is a * before stack[:all].map(&:to_sym) , this is to convert the array into individual arguments.如果你想知道为什么在stack[:all].map(&:to_sym)之前有一个* ,这是为了将数组转换为单独的参数。 In this context * is called the spat operator .在这种情况下, *称为spat 运算符

You might write the code as follows.您可以按如下方式编写代码。

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

When, for example, `k = "mango",例如,当 `k = "mango" 时,

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

I've defined the local variable h for three reasons:我出于三个原因定义了局部变量h

  • it simplifies the code by avoiding multiple references to stack[k.to_sym]它通过避免多次引用stack[k.to_sym]来简化代码
  • when debugging it may may be helpful to be able to examine h在调试时,能够检查h可能会有所帮助
  • it makes the code more readable它使代码更具可读性

Note that h merely holds an existing hash;请注意, h仅保存现有的哈希值; it does not create a copy of that hash, so it has a neglibile effect on memory requirements.它不会创建该哈希的副本,因此它对内存需求的影响可以忽略不计。

The technique of defining local variables to hold objects that are parts of other objects is especially useful for more complex objects.定义局部变量以保存属于其他对象的对象的技术对于更复杂的对象尤其有用。 Suppose, for example, we had the hash例如,假设我们有散列

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

Now suppose we wish to add a dog toy现在假设我们想添加一个狗玩具

new_toy = "frisbee"

if it is not already present in the array如果它不存在于数组中

hash[:dog][:toys]

We could write我们可以写

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"]}} 

Alternatively, we could write或者,我们可以写

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"]}} 
 

Not only does the latter snippet display intermediate results, it probably is a wash with the first snippet in terms of execution speed and storage requirements and arguably is more readable.后一个片段不仅显示中间结果,而且在执行速度和存储要求方面可能是第一个片段的清洗,并且可以说更具可读性。 It also cuts down on careless mistakes such as它还减少了粗心的错误,例如

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

If one element of stack[:all] were, for example, "pineapple" , stack[:pineapple] #=> nil since stack has no key :pineapple .例如,如果stack[:all]一个元素是"pineapple"stack[:pineapple] #=> nil因为stack没有键:pineapple If, however, stack contained the key-value pair但是,如果stack包含键值对

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

that would become problem.那将成为问题。 Far-fetched?牵强? Maybe, but it is perhaps good practice--in part for readability--to avoid the assumption that h[k] #=> nil means h has no key k ;也许吧,但这也许是一种很好的做法——部分是为了可读性——避免假设h[k] #=> nil意味着h没有键k instead, use if h.key?(k) .相反,使用if h.key?(k) For example:例如:

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