Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/ruby-on-rails/53.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ruby on rails 使用Ruby/Rails将嵌套哈希展平为单个哈希_Ruby On Rails_Ruby_Recursion - Fatal编程技术网

Ruby on rails 使用Ruby/Rails将嵌套哈希展平为单个哈希

Ruby on rails 使用Ruby/Rails将嵌套哈希展平为单个哈希,ruby-on-rails,ruby,recursion,Ruby On Rails,Ruby,Recursion,我想“展平”(不是经典意义上的。展平)一个深度不同的散列,如下所示: { :foo => "bar", :hello => { :world => "Hello World", :bro => "What's up dude?", }, :a => { :b => { :c => "d" } } } 向下转换为一个具有一个级别的哈希,所有嵌套键合并为一个字符串,因此它将变成: { :f

我想“展平”(不是经典意义上的
。展平
)一个深度不同的散列,如下所示:

{
  :foo => "bar",
  :hello => {
    :world => "Hello World",
    :bro => "What's up dude?",
  },
  :a => {
    :b => {
      :c => "d"
    }
  }
}
向下转换为一个具有一个级别的哈希,所有嵌套键合并为一个字符串,因此它将变成:

{
  :foo => "bar",
  :"hello.world" => "Hello World",
  :"hello.bro" => "What's up dude?",
  :"a.b.c" => "d"
}
但我想不出一个好办法。这有点像Rails添加到散列中的
deep\uu
helper函数,但并不完全相同。我知道递归是一种方法,但我从未用Ruby编写过递归函数。

您可以这样做:

def flatten_hash(hash)
  hash.each_with_object({}) do |(k, v), h|
    if v.is_a? Hash
      flatten_hash(v).map do |h_k, h_v|
        h["#{k}.#{h_k}".to_sym] = h_v
      end
    else 
      h[k] = v
    end
   end
end

flatten_hash(:foo => "bar",
  :hello => {
    :world => "Hello World",
    :bro => "What's up dude?",
  },
  :a => {
    :b => {
      :c => "d"
    }
  })
# => {:foo=>"bar", 
# =>  :"hello.world"=>"Hello World", 
# =>  :"hello.bro"=>"What's up dude?", 
# =>  :"a.b.c"=>"d"} 

或者,如果您想要一个经过修补的猴子版本或Uri的答案转到您的\u散列。将\u展平到\u根目录:

class Hash
  def flatten_to_root
    self.each_with_object({}) do |(k, v), h|
      if v.is_a? Hash
        v.flatten_to_root.map do |h_k, h_v|
          h["#{k}.#{h_k}".to_sym] = h_v
        end
      else
        h[k] = v
      end
    end
  end
end

因为我喜欢
Enumerable#reduce
而且显然讨厌行:

def flatten_hash(param, prefix=nil)
  param.each_pair.reduce({}) do |a, (k, v)|
    v.is_a?(Hash) ? a.merge(flatten_hash(v, "#{prefix}#{k}.")) : a.merge("#{prefix}#{k}".to_sym => v)
  end
end


这里的最上面的答案不会一直展平对象,它不会展平阵列。我在下面更正了这一点,并提供了一个比较:

x = { x: 0, y: { x: 1 }, z: [ { y: 0, x: 2 }, 4 ] }

def top_voter_function ( hash )
  hash.each_with_object( {} ) do |( k, v ), h|
    if v.is_a? Hash
      top_voter_function( v ).map do |h_k, h_v|
        h[ "#{k}.#{h_k}".to_sym ] = h_v
      end
    else
      h[k] = v
    end
  end
end

def better_function ( a_el, a_k = nil )
  result = {}

  a_el = a_el.as_json

  a_el.map do |k, v|
    k = "#{a_k}.#{k}" if a_k.present?
    result.merge!( [Hash, Array].include?( v.class ) ? better_function( v, k ) : ( { k => v } ) )
  end if a_el.is_a?( Hash )

  a_el.uniq.each_with_index do |o, i|
    i = "#{a_k}.#{i}" if a_k.present?
    result.merge!( [Hash, Array].include?( o.class ) ? better_function( o, i ) : ( { i => o } ) )
  end if a_el.is_a?( Array )

  result
end

top_voter_function( x ) #=> {:x=>0, :"y.x"=>1, :z=>[{:y=>0, :x=>2}, 4]}
better_function( x ) #=> {"x"=>0, "y.x"=>1, "z.0.y"=>0, "z.0.x"=>2, "z.1"=>4} 

我很感激这个问题有点老了,我在网上寻找我上面代码的比较,这就是我发现的。它与Mixpanel等分析服务的事件一起使用时效果非常好。

在我的例子中,我使用的是Parameters类,因此上述解决方案都不适用于我。我解决问题的方法是创建以下函数:

def flatten_params(param, extracted = {})
    param.each do |key, value|
        if value.is_a? ActionController::Parameters
            flatten_params(value, extracted)
        else
            extracted.merge!("#{key}": value)
        end
    end
    extracted
end

然后您可以像
展平参数=展平参数(params)
那样使用它。希望这有帮助。

如果您在对象中转换哈希值(Rails的方式),它会有用吗?编辑:我现在意识到它不会在对象中递归地执行它。但这似乎是一个解决办法。太棒了,这帮我省去了一天的头疼。而且很好,很简单,非常好。可能你想把地图换成一张。谢谢!超级清洁解决方案作为提示,我建议使用gem“人性化”使数字更具可读性。谢谢。不过,我的问题是专门针对散列的展平,而我当时实际上不希望在我的用例中展平数组。不用担心。实际上,在原始问题中根本没有提到数组。谢谢你的提交,我发现这非常有用!
def flatten_params(param, extracted = {})
    param.each do |key, value|
        if value.is_a? ActionController::Parameters
            flatten_params(value, extracted)
        else
            extracted.merge!("#{key}": value)
        end
    end
    extracted
end