将嵌套哈希数组减少为平坦的键/值对数组

时间:2017-07-29 00:07:33

标签: ruby

鉴于以下数据结构:

[
    [:created_at, "07/28/2017"],
    [:valid_record, "true"],
    [:cs_details, { gender: 'm', race: 'w', language: nil } ],
    [:co_details, { description: 'possess', extra: { a: 'a', b: 'b', c: 'c'} } ]
]

我想要一组键/值对数组:

[
        [:created_at, "07/28/2017"],
        [:valid_record, "true"],
        [:gender, 'm'],
        [:race, 'w'],
        [:description, "process"]
        [:a, "a"],
        [:b, "b"],
        [:c, "c"]
]

问题是我不知道如何压扁这些哈希。 flatten没有做任何事情:

arr.map(&:flatten)
 => [[:created_at, "07/28/2017"], [:valid_record, "true"], [:cs_details, {:gender=>"m", :race=>"w", :language=>nil}], [:co_details, {:description=>"possess", :extra=>{:a=>"a", :b=>"b", :c=>"c"}}]] 

所以我知道flat_map也无济于事。我甚至无法使用to_a将这些哈希值转换为数组:

arr.map(&:to_a)
 => [[:created_at, "07/28/2017"], [:valid_record, "true"], [:cs_details, {:gender=>"m", :race=>"w", :language=>nil}], [:co_details, {:description=>"possess", :extra=>{:a=>"a", :b=>"b", :c=>"c"}}]] 

上述方法的问题在于它们只能处理顶级索引。这些哈希嵌套在数组中。所以我尝试reduce然后在result:

上调用flat_map
arr.reduce([]) do |acc, (k,v)|
  if v.is_a?(Hash)
    acc << v.map(&:to_a)
  else
    acc << [k,v]
  end
  acc
end.flat_map(&:to_a)
=> [:created_at, "07/28/2017", :valid_record, "true", [:gender, "m"], [:race, "w"], [:language, nil], [:description, "possess"], [:extra, {:a=>"a", :b=>"b", :c=>"c"}]] 

不太相似,但更接近。有什么建议吗?

3 个答案:

答案 0 :(得分:2)

▶ flattener = ->(k, v) do
▷   case v
▷   when Enumerable then v.flat_map(&flattener)  
▷   when NilClass then []  
▷   else [k, v]  
▷   end  
▷ end  
#⇒ #<Proc:0x000000032169e0@(pry):26 (lambda)>
▶ input.flat_map(&flattener).each_slice(2).to_a
#⇒ [
#    [:created_at, "07/28/2017"],
#    [:valid_record, "true"],
#    [:gender, "m"],
#    [:race, "w"],
#    [:description, "possess"],
#    [:a, "a"],
#    [:b, "b"],
#    [:c, "c"]
#  ]

答案 1 :(得分:1)

我认为你会从编写一个将在数组中的每个项目上调用的辅助函数中受益。为了使结果一致,我们将确保此函数始终返回一个数组数组。换句话说,一个包含一个或多个&#34;条目的数组,&#34;取决于索引1处的东西是否为散列。

def extract_entries((k,v))
  if v.is_a? Hash
    v.to_a
  else
    [[k, v]]
  end
end

尝试一下:

require 'pp'

pp data.map {|item| extract_entries(item)}

输出:

[[[:created_at, "07/28/2017"]],
 [[:valid_record, "true"]],
 [[:gender, "m"], [:race, "w"], [:language, nil]],
 [[:description, "possess"], [:extra, {:a=>"a", :b=>"b", :c=>"c"}]]]

现在,我们可以按一个级别展平,以达到您想要的格式:

pp data.map {|item| extract_entries(item)}.flatten(1)

输出:

[[:created_at, "07/28/2017"],
 [:valid_record, "true"],
 [:gender, "m"],
 [:race, "w"],
 [:language, nil],
 [:description, "possess"],
 [:extra, {:a=>"a", :b=>"b", :c=>"c"}]]

答案 2 :(得分:0)

def to_flat(arr)
  arr.flat_map { |k,v| v.is_a?(Hash) ? to_flat(v.compact) : [[k, v]] }
end

测试

arr = [ [:created_at, "07/28/2017"],
        [:valid_record, "true"],
        [:cs_details, { gender: 'm', race: 'w', language: nil } ],
        [:co_details, { description: 'possess', extra: { a: 'a', b: 'b', c: 'c'} } ] ]
to_flat(arr)
#=> [[:created_at, "07/28/2017"], [:valid_record, "true"], [:gender, "m"],
#    [:race, "w"], [:description, "possess"], [:a, "a"], [:b, "b"], [:c, "c"]]