使用Ruby/Rails将嵌套哈希扁平化为单个哈希

h7wcgrx3  于 2023-08-04  发布在  Ruby
关注(0)|答案(7)|浏览(122)

我想用不同的深度来“扁平化”(不是传统意义上的.flatten)一个哈希,像这样:

{
  :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添加到Hash中的deep_辅助函数,但不完全相同。我知道递归是一种方法,但我从来没有用Ruby写过递归函数。

vx6bjr1n

vx6bjr1n1#

你可以这样做:

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

字符串

flmtquvp

flmtquvp2#

因为我喜欢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

个字符

prdp8dxp

prdp8dxp3#

这里投票最高的答案不会一直展平对象,它不会展平数组。我在下面纠正了这个问题,并提供了一个比较:

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等分析服务的事件一起使用时,效果非常好。

liwlm1x9

liwlm1x94#

或者,如果你想要一个猴子补丁的版本或Uri的答案去your_hash.flatten_to_root

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

字符串

ulydmbyx

ulydmbyx5#

在我的例子中,我使用的是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

字符串
然后你可以像flatten_parameters = flatten_params(params)一样使用它。希望这对你有帮助。

eivnm1vs

eivnm1vs6#

以防万一你想留住他们的父母

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

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

flatten_hash(hash)

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

字符串

rdlzhqv9

rdlzhqv97#

这里有一个对我有效的解决方案:

class Hash
  # Test with:
  # {
  #   s: 1,
  #   s2: '2',
  #   n1: {},
  #   n2: { a: 1 },
  #   n3: { a: { b: 1, c: [2, 3], d: { e: 1, f: [4, 5, 'x'] } } },
  #   n4: [],
  #   n5: [1],
  #   n6: [[[[[[[['treasure', [], 1, 'n', {}, { a: 1, b: [2, 3], c: { d: 1, e: [4, 5] } }]]]]]]]],
  #   n7: [{ a: 1, b: [2, 3], c: { d: 1, e: [4, 5] } }, 'y'],
  # }.to_dotted_keys
  def to_dotted_keys(parent_key = nil, flattened_hash = {})
    each do |key, value|
      current_key = parent_key ? "#{parent_key}.#{key}" : key.to_s

      if value.is_a?(Hash) && !value.empty?
        value.to_dotted_keys(current_key, flattened_hash)
      elsif value.is_a?(Array) && !value.empty?
        value.each_with_index do |item, index|
          if item.is_a?(Hash) || item.is_a?(Array)
            { index => item }.to_dotted_keys(current_key, flattened_hash)
          else
            flattened_hash["#{current_key}.#{index}"] = item
          end
        end
      else
        flattened_hash[current_key] = value
      end
    end

    flattened_hash
  end
end

字符串

相关问题