What is the most ruby-ish way of accessing nested

2019-02-13 03:25发布

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().

4条回答
走好不送
2楼-- · 2019-02-13 03:39
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.

查看更多
迷人小祖宗
3楼-- · 2019-02-13 03:51

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.

查看更多
【Aperson】
4楼-- · 2019-02-13 03:56
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"
查看更多
Animai°情兽
5楼-- · 2019-02-13 04:00
%w[other service secret].inject(AppConfig, &:fetch)
查看更多
登录 后发表回答