问题
Given a hash such as:
AppConfig = {
'service' => {
'key' => 'abcdefg',
'secret' => 'secret_abcdefg'
},
'other' => {
'service' => {
'key' => 'cred_abcdefg',
'secret' => 'cred_secret_abcdefg'
}
}
}
I need a function to return service/key in some cases and other/service/key in other cases. A straightforward way is to pass in the hash and an array of keys, like so:
def val_for(hash, array_of_key_names)
h = hash
array_of_key_names.each { |k| h = h[k] }
h
end
So that this call results in 'cred_secret_abcdefg':
val_for(AppConfig, %w[other service secret])
It seems like there should be a better way than what I've written in val_for().
回答1:
def val_for(hash, keys)
keys.reduce(hash) { |h, key| h[key] }
end
This will raise an exception if some intermediate key is not found. Note also that this is completely equivalent to keys.reduce(hash, :[])
, but this may very well confuse some readers, I'd use the block.
回答2:
%w[other service secret].inject(AppConfig, &:fetch)
回答3:
appConfig = {
'service' => {
'key' => 'abcdefg',
'secret' => 'secret_abcdefg'
},
'other' => {
'service' => {
'key' => 'cred_abcdefg',
'secret' => 'cred_secret_abcdefg'
}
}
}
def val_for(hash, array_of_key_names)
eval "hash#{array_of_key_names.map {|key| "[\"#{key}\"]"}.join}"
end
val_for(appConfig, %w[other service secret]) # => "cred_secret_abcdefg"
回答4:
Ruby 2.3.0 introduced a new method called dig on both Hash
and Array
that solves this problem entirely.
AppConfig.dig('other', 'service', 'secret')
It returns nil
if the key is missing at any level.
来源:https://stackoverflow.com/questions/13259181/what-is-the-most-ruby-ish-way-of-accessing-nested-hash-values-at-arbitrary-depth