嵌套哈希键

时间:2015-05-30 21:46:20

标签: ruby recursion nested traversal

将点表示法路径(甚至字符串数组)转换为嵌套哈希键值的最佳方法是什么?例如:我需要将'foo.bar.baz'转换为'qux',如下所示:

{
    'foo' => {
        'bar' => {
            'baz' => 'qux'
        }
    }
}

我已经在PHP中完成了这项工作,但我通过在数组中创建一个键然后通过引用将tmp变量设置为该数组键的值来管理它,因此任何更改也会在阵列。

4 个答案:

答案 0 :(得分:8)

试试这个

f = "root/sub-1/sub-2/file"   
f.split("/").reverse.inject{|a,n| {n=>a}} #=>{"root"=>{"sub-1"=>{"sub-2"=>"file"}}}

答案 1 :(得分:4)

我可能会使用递归。例如:

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"}}}

答案 2 :(得分:0)

当我编写一个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 ......看着这样的代码,我想知道我在想什么......

答案 3 :(得分:0)

我喜欢下面的这种方法,该方法可以对自身(或您自己的哈希类)进行操作。它将创建新的哈希键或重复使用/追加到哈希中的现有键以添加或更新值。

def rep_words(novel_list):
    rep_num=0
    for i in novel_list:
        if novel_list.count(i)>rep_num:
            rep_num=novel_list.count(i)
    return rep_num

用法:

# 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