Ruby 嵌套哈希键的点符号
Ruby dot notation to nested hash keys
将点符号路径(甚至字符串数组)转换为嵌套哈希键值的最佳方法是什么?例如:我需要像这样将 'foo.bar.baz'
转换为 'qux'
:
{
'foo' => {
'bar' => {
'baz' => 'qux'
}
}
}
我在 PHP 中完成了此操作,但我通过在数组中创建一个键然后通过引用将 tmp 变量设置为该数组键的值来管理,因此任何更改也会发生在数组。
我可能会使用递归。例如:
def hasherizer(arr, value)
if arr.empty?
value
else
{}.tap do |hash|
hash[arr.shift] = hasherizer(arr, value)
end
end
end
这导致:
> hasherizer 'foo.bar.baz'.split('.'), 'qux'
=> {"foo"=>{"bar"=>{"baz"=>"qux"}}}
试试这个
f = "root/sub-1/sub-2/file"
f.split("/").reverse.inject{|a,n| {n=>a}} #=>{"root"=>{"sub-1"=>{"sub-2"=>"file"}}}
当我编写一个 HTTP 服务器时,我做了类似的事情,该服务器必须将请求中传递的所有参数移动到一个可能包含数组或字符串或哈希的多值哈希中...
您可以查看 Plezi server and framework... 的代码,尽管那里的代码处理的是 []
...
包围的值
可以这样调整:
def add_param_to_hash param_name, param_value, target_hash = {}
begin
a = target_hash
p = param_name.split(/[\/\.]/)
val = param_value
# the following, somewhat complex line, runs through the existing (?) tree, making sure to preserve existing values and add values where needed.
p.each_index { |i| p[i].strip! ; n = p[i].match(/^[0-9]+$/) ? p[i].to_i : p[i].to_sym ; p[i+1] ? [ ( a[n] ||= ( p[i+1].empty? ? [] : {} ) ), ( a = a[n]) ] : ( a.is_a?(Hash) ? (a[n] ? (a[n].is_a?(Array) ? (a << val) : a[n] = [a[n], val] ) : (a[n] = val) ) : (a << val) ) }
rescue Exception => e
warn "(Silent): parameters parse error for #{param_name} ... maybe conflicts with a different set?"
target_hash[param_name] = param_value
end
end
这应该保留现有值,同时添加新值(如果存在)。
长线分解后看起来像这样:
def add_param_to_hash param_name, param_value, target_hash = {}
begin
# a will hold the object to which we Add.
# As we walk the tree we change `a`. we start at the root...
a = target_hash
p = param_name.split(/[\/\.]/)
val = param_value
# the following, somewhat complex line, runs through the existing (?) tree, making sure to preserve existing values and add values where needed.
p.each_index do |i|
p[i].strip!
# converts the current key string to either numbers or symbols... you might want to replace this with: n=p[i]
n = p[i].match(/^[0-9]+$/) ? p[i].to_i : p[i].to_sym
if p[i+1]
a[n] ||= ( p[i+1].empty? ? [] : {} ) # is the new object we'll add to
a = a[n] # move to the next branch.
else
if a.is_a?(Hash)
if a[n]
if a[n].is_a?(Array)
a << val
else
a[n] = [a[n], val]
end
else
a[n] = val
end
else
a << val
end
end
end
rescue Exception => e
warn "(Silent): parameters parse error for #{param_name} ... maybe conflicts with a different set?"
target_hash[param_name] = param_value
end
end
Brrr...看着这样的代码,我不知道我在想什么...
我喜欢下面这种对自身(或您自己的散列 class )进行操作的方法。它将创建新的哈希键或 reuse/append 到哈希中的现有键以添加或更新值。
# set a new or existing nested key's value by a dotted-string key
def dotkey_set(dottedkey, value, deep_hash = self)
keys = dottedkey.to_s.split('.')
first = keys.first
if keys.length == 1
deep_hash[first] = value
else
# in the case that we are creating a hash from a dotted key, we'll assign a default
deep_hash[first] = (deep_hash[first] || {})
dotkey_set(keys.slice(1..-1).join('.'), value, deep_hash[first])
end
end
用法:
hash = {}
hash.dotkey_set('how.are.you', 'good')
# => "good"
hash
# => {"how"=>{"are"=>{"you"=>"good"}}}
hash.dotkey_set('how.goes.it', 'fine')
# => "fine"
hash
# => {"how"=>{"are"=>{"you"=>"good"}, "goes"=>{"it"=>"fine"}}}
将点符号路径(甚至字符串数组)转换为嵌套哈希键值的最佳方法是什么?例如:我需要像这样将 'foo.bar.baz'
转换为 'qux'
:
{
'foo' => {
'bar' => {
'baz' => 'qux'
}
}
}
我在 PHP 中完成了此操作,但我通过在数组中创建一个键然后通过引用将 tmp 变量设置为该数组键的值来管理,因此任何更改也会发生在数组。
我可能会使用递归。例如:
def hasherizer(arr, value)
if arr.empty?
value
else
{}.tap do |hash|
hash[arr.shift] = hasherizer(arr, value)
end
end
end
这导致:
> hasherizer 'foo.bar.baz'.split('.'), 'qux'
=> {"foo"=>{"bar"=>{"baz"=>"qux"}}}
试试这个
f = "root/sub-1/sub-2/file"
f.split("/").reverse.inject{|a,n| {n=>a}} #=>{"root"=>{"sub-1"=>{"sub-2"=>"file"}}}
当我编写一个 HTTP 服务器时,我做了类似的事情,该服务器必须将请求中传递的所有参数移动到一个可能包含数组或字符串或哈希的多值哈希中...
您可以查看 Plezi server and framework... 的代码,尽管那里的代码处理的是 []
...
可以这样调整:
def add_param_to_hash param_name, param_value, target_hash = {}
begin
a = target_hash
p = param_name.split(/[\/\.]/)
val = param_value
# the following, somewhat complex line, runs through the existing (?) tree, making sure to preserve existing values and add values where needed.
p.each_index { |i| p[i].strip! ; n = p[i].match(/^[0-9]+$/) ? p[i].to_i : p[i].to_sym ; p[i+1] ? [ ( a[n] ||= ( p[i+1].empty? ? [] : {} ) ), ( a = a[n]) ] : ( a.is_a?(Hash) ? (a[n] ? (a[n].is_a?(Array) ? (a << val) : a[n] = [a[n], val] ) : (a[n] = val) ) : (a << val) ) }
rescue Exception => e
warn "(Silent): parameters parse error for #{param_name} ... maybe conflicts with a different set?"
target_hash[param_name] = param_value
end
end
这应该保留现有值,同时添加新值(如果存在)。
长线分解后看起来像这样:
def add_param_to_hash param_name, param_value, target_hash = {}
begin
# a will hold the object to which we Add.
# As we walk the tree we change `a`. we start at the root...
a = target_hash
p = param_name.split(/[\/\.]/)
val = param_value
# the following, somewhat complex line, runs through the existing (?) tree, making sure to preserve existing values and add values where needed.
p.each_index do |i|
p[i].strip!
# converts the current key string to either numbers or symbols... you might want to replace this with: n=p[i]
n = p[i].match(/^[0-9]+$/) ? p[i].to_i : p[i].to_sym
if p[i+1]
a[n] ||= ( p[i+1].empty? ? [] : {} ) # is the new object we'll add to
a = a[n] # move to the next branch.
else
if a.is_a?(Hash)
if a[n]
if a[n].is_a?(Array)
a << val
else
a[n] = [a[n], val]
end
else
a[n] = val
end
else
a << val
end
end
end
rescue Exception => e
warn "(Silent): parameters parse error for #{param_name} ... maybe conflicts with a different set?"
target_hash[param_name] = param_value
end
end
Brrr...看着这样的代码,我不知道我在想什么...
我喜欢下面这种对自身(或您自己的散列 class )进行操作的方法。它将创建新的哈希键或 reuse/append 到哈希中的现有键以添加或更新值。
# set a new or existing nested key's value by a dotted-string key
def dotkey_set(dottedkey, value, deep_hash = self)
keys = dottedkey.to_s.split('.')
first = keys.first
if keys.length == 1
deep_hash[first] = value
else
# in the case that we are creating a hash from a dotted key, we'll assign a default
deep_hash[first] = (deep_hash[first] || {})
dotkey_set(keys.slice(1..-1).join('.'), value, deep_hash[first])
end
end
用法:
hash = {}
hash.dotkey_set('how.are.you', 'good')
# => "good"
hash
# => {"how"=>{"are"=>{"you"=>"good"}}}
hash.dotkey_set('how.goes.it', 'fine')
# => "fine"
hash
# => {"how"=>{"are"=>{"you"=>"good"}, "goes"=>{"it"=>"fine"}}}