Безопасное присвоение значения вложенному хэшу с помощью оператора Hash # dig или Lonely (&.)
h = {
data: {
user: {
value: "John Doe"
}
}
}
Чтобы присвоить значение вложенному хешу, мы можем использовать
h[:data][:user][:value] = "Bob"
Однако, если какая-либо часть в середине отсутствует, это вызовет ошибку.
Что-то вроде
h.dig(:data, :user, :value) = "Bob"
не будет работать, поскольку пока нет Hash#dig=
.
Чтобы безопасно присвоить значение, мы можем сделать
h.dig(:data, :user)&.[]=(:value, "Bob") # or equivalently
h.dig(:data, :user)&.store(:value, "Bob")
Но есть ли лучший способ сделать это?
Ответы
Ответ 1
Это не без его оговорок (и не работает, если вы получаете хэш из других источников), но общее решение таково:
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
Я нашел простое решение для установки значения вложенного хэша, даже если родительский ключ отсутствует, даже если хэш уже существует. Дано:
x = { gojira: { guitar: { joe: 'charvel' } } }
Предположим, вы хотите включить барабан Марио, чтобы в результате:
x = { gojira: { guitar: { joe: 'charvel' }, drum: { mario: 'tama' } } }
Я закончил тем, что исправлял хэш:
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
использование:
x.nested_set([:instrument, :drum, :mario], 'tama')
использование для вашего примера:
h.nested_set([:data, :user, :value], 'Bob')
какие-либо предостережения, которые я пропустил? Есть ли лучший способ написать код, не жертвуя читабельностью?
Ответ 3
интересный:
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
в любом случае вызовет исключение, если нет методов []
или []=
.