收集具有相同值的哈希条目

Collecting hash entries with identical values

我有一个如下所示的数组

unorganized_array = [
  {:identifier => '1', :groupinfo => [{:color => 'blue',  :texture => 'soft'}]},
  {:identifier => '1', :groupinfo => [{:color => 'green', :texture => 'hard'}]},
  {:identifier => '2', :groupinfo => [{:color => 'red',   :texture => 'spiky'}]}]

[{:identifier => '1', :groupinfo => [ {:color => 'blue', :texture => 'soft'}]}, {:identifier => '1', :groupinfo => [ {:color => 'green', :texture => 'hard'}]}, {{:identifier => '2', :groupinfo => [{:color => 'red', :texture => 'spiky'}]}

我想将具有相同 :identifier 的所有条目收集到该标识符的 :groupinfo 中。与前面的示例相比,这有一个额外的 :identifier => '2' 组:

organized_array = [{:identifier => '1', :groupinfo => [
{:color => 'blue', :texture => 'soft'}, 
{:color => 'green', :texture => 'hard'}]},
{:identifier => '2', :groupinfo =>
[{:color => 'red', :texture => 'spiky'},
{:color => 'gray', :texture => 'squishy}]}]

我觉得 Hash#mergeHash#inject 在这里很有用,但我不确定如何实现它们。

我正在从一个看起来像

的数组中生成 unorganized_array
original_array = [['blue', 'soft', '1', 'irrelevant'], ['green','hard','1','irrelevant1'],
['red','spiky','2','irrelevant2']]

也许有比从 original_array -> unorganized_array -> organized_array?

更好的方法

到目前为止,我一直在尝试使用带有 for 循环的#map 和#each 将它们组合在一起,即

unorganized_array = original_array.map! do |first, second, third, fourth|
    {:identifier => third, :groupinfo => [{:color => first, :texture => second}]}
end
unorganized_array.map(&:dup)
                 .group_by { |e| e.delete(:identifier) }
                 .map { |k, v| [k, v.flat_map { |h| h[:groupinfo] } ] }
                 .map { |k, v| { identifier: k, groupinfo: v } }

上面给出了显示的输入:

#⇒ [ { :groupinfo => [ 
#         { :color => "blue", :texture => "soft" },
#         { :color => "green", :texture => "hard" } ],
#      :identifier => "1" },
#    { :groupinfo => [
#         { :color => "red", :texture => "spiky" } ],
#      :identifier => "2" } ]

目前,您正在生成一个双键哈希数组,其中第二个包含一个属性数组。为什么不使用 "identifier" 作为 key 来代替 Hash?毕竟,这正是关键所在。

这看起来更干净:

{"1"=>[{:color=>"blue", :texture=>"soft"}, 
       {:color=>"green", :texture=>"hard"}], 
"2"=>[{:color=>"red", :texture=>"spiky"}]}

这是一种生成方法:

my_hash = original_array.reduce({}) do |r,s| 
  r.merge( s[2] => (r[s[2]] || []) + [{color: s[0], texture: s[1]}] )
end

对于@the Tin Man 的观点,理解 Ruby 的 Enumerable class 比发现我们可以提供的任何特定解决方案更有价值 objective。


使用你的哈希

这是一种遍历所有项目的方法。

my_hash.each do |key,arr| 
  puts "Identifier \##{key} has #{arr.size} items"
  arr.each_with_index do |item,index|
    puts "Item \##{index+1} is #{item[:color]} and #{item[:texture]}"
  end
end

输出:

Identifier #1 has 2 items
Item #1 is blue and soft
Item #2 is green and hard
Identifier #2 has 1 items
Item #1 is red and spiky

我认为这不是最好的方法,但我建议这样做:

1- 按标识符对数组进行分组

unorganized_array.group_by{|x| x[:identifier]}
#=>  {"1"=>[{:identifier=>"1", :groupinfo=>[{:color=>"blue", :texture=>"soft"}]},
{:identifier=>"1", :groupinfo=>[{:color=>"green", :texture=>"hard"}]}],
"2"=>[{:identifier=>"2", :groupinfo=>[{:color=>"red", :texture=>"spiky"}]}]}

2 - 在预期的散列中收集您的 grouped_array 元素并收集组信息值。现在数组中的每个元素都包含相关项。

group_array.each.collect {|k,g| { :identifier => k, :group_info => g.collect{ |x| x[:groupinfo].last } } }

3-结果是组织好的数组

[{:identifier=>"1", :group_info=>[
 {:color=>"blue", :texture=>"soft"}, 
 {:color=>"green", :texture=>"hard"}
]}, 
{:identifier=>"2", :group_info=>[{:color=>"red", :texture=>"spiky"}]}]
arr =
  [{:identifier => '1', :groupinfo => [{:color => 'blue,   :texture => 'soft}]},
   {:identifier => '1', :groupinfo => [{:color => 'green', :texture => 'hard'}]},
   {:identifier => '2', :groupinfo => [{:color => 'red',   :texture => 'spiky'}]}]

有几种方法可以通过构建哈希来实现。

使用 Hash::new 的形式,当散列 h 没有键 k 时,使用默认值确定 h[k]

hash_with_default = Hash.new { |h,k| { identifier: k, groupdata: [] } }

arr.each_with_object(hash_with_default) { |g,h| h[g[:identifier]] =
  { identifier: g[:identifier],
    groupdata:  h[g[:identifier]][:groupdata] << g[:groupinfo].first } }.values
  #=> [{:identifier=>"1", :groupinfo=>[{:color=>"blue", :texture=>"soft"},
  #                                    {:color=>"green", :texture=>"hard"}]},
  #    {:identifier=>"2", :groupinfo=>[{:color=>"red", :texture=>"spiky"}]}] 

使用 Hash#update(又名 merge!)的形式,它使用一个块来确定要合并的两个散列中存在的键的值

arr.each_with_object({}) { |g,h| h.update(g[:identifier] => g) { |k,o,n|
  { identifier: o[:identifier], groupinfo: h[k][:groupinfo] + g[:groupinfo] } } }.values
  #=> [{:identifier=>"1", :groupinfo=>[{:color=>"blue", :texture=>"soft"},
  #                                    {:color=>"green", :texture=>"hard"}]},
  #    {:identifier=>"2", :groupinfo=>[{:color=>"red", :texture=>"spiky"}]}] 

kon

三个块变量的含义请查阅文档