Safely assign value to nested hash using Hash#dig or Lonely operator(&.)

心已入冬 提交于 2019-12-18 12:14:39

问题


h = {
  data: {
    user: {
      value: "John Doe" 
    }
  }
}

To assign value to the nested hash, we can use

h[:data][:user][:value] = "Bob"

However if any part in the middle is missing, it will cause error.

Something like

h.dig(:data, :user, :value) = "Bob"

won't work, since there's no Hash#dig= available yet.

To safely assign value, we can do

h.dig(:data, :user)&.[]=(:value, "Bob")    # or equivalently
h.dig(:data, :user)&.store(:value, "Bob")

But is there better way to do that?


回答1:


It's not without its caveats (and doesn't work if you're receiving the hash from elsewhere), but a common solution is this:

hash = Hash.new {|h,k| h[k] = h.class.new(&h.default_proc) }

hash[:data][:user][:value] = "Bob"
p hash
# => { :data => { :user => { :value => "Bob" } } }



回答2:


I found a simple solution to set the value of a nested hash, even if a parent key is missing, even if the hash already exists. Given:

x = { gojira: { guitar: { joe: 'charvel' } } }

Suppose you wanted to include mario's drum to result in:

x = { gojira: { guitar: { joe: 'charvel' }, drum: { mario: 'tama' } } }

I ended up monkey-patching Hash:

class Hash

    # ensures nested hash from keys, and sets final key to value
    # keys: Array of Symbol|String
    # value: any
    def nested_set(keys, value)
      raise "DEBUG: nested_set keys must be an Array" unless keys.is_a?(Array)

      final_key = keys.pop
      return unless valid_key?(final_key)
      position = self
      for key in keys
        return unless valid_key?(key)
        position[key] = {} unless position[key].is_a?(Hash)
        position = position[key]
      end
      position[final_key] = value
    end

    private

      # returns true if key is valid
      def valid_key?(key)
        return true if key.is_a?(Symbol) || key.is_a?(String)
        raise "DEBUG: nested_set invalid key: #{key} (#{key.class})"
      end
end

usage:

x.nested_set([:instrument, :drum, :mario], 'tama')

usage for your example:

h.nested_set([:data, :user, :value], 'Bob')

any caveats i missed? any better way to write the code without sacrificing readability?




回答3:


interesting one:

def dig_set(obj, keys, value)
  if keys.length == 1
    obj[keys.first] = value
  else
    dig_set(obj[keys.first], keys.slice(1..-1), value)
  end
end

will raise an exception anyways if there's no [] or []= methods.



来源:https://stackoverflow.com/questions/34620469/safely-assign-value-to-nested-hash-using-hashdig-or-lonely-operator

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!