Ruby 哈希数组。 group_by 并在一行中修改

Posted

技术标签:

【中文标题】Ruby 哈希数组。 group_by 并在一行中修改【英文标题】:Ruby array of hash. group_by and modify in one line 【发布时间】:2013-09-26 02:01:21 【问题描述】:

我有一个哈希数组,类似于

[ :type=>"Meat", :name=>"one", 
  :type=>"Meat", :name=>"two", 
  :type=>"Fruit", :name=>"four" ]

我想把它转换成这个

 "Meat" => ["one", "two"], "Fruit" => ["Four"]

我试过group_by 但后来我得到了这个

 "Meat" => [:type=>"Meat", :name=>"one", :type=>"Meat", :name=>"two"],
  "Fruit" => [:type=>"Fruit", :name=>"four"] 

然后我不能修改它只留下名称而不是完整的哈希。我需要在一行中执行此操作,因为这是针对 Rails 表单上的grouped_options_for_select

【问题讨论】:

有一个option_groups_from_collection_for_select 助手。 【参考方案1】:
array.group_by|h| h[:type].each|_, v| v.replace(v.map|h| h[:name])
# => "Meat"=>["one", "two"], "Fruit"=>["four"]

听从 steenslag 的建议:

array.group_by|h| h[:type].each|_, v| v.map!|h| h[:name]
# => "Meat"=>["one", "two"], "Fruit"=>["four"]

【讨论】:

最后一个block可以写成|_, v| v.map!|h| h[:name] @steenslag 谢谢。我忘记了。 ...或each_value...,不明白为什么! .each_value |v| v.map!|h| h[:name] 有效,但我看到! 是必需的。【参考方案2】:

在初始数组的单次迭代中:

arry.inject(Hash.new([]))  |h, a| h[a[:type]] += [a[:name]]; h 

【讨论】:

不错! (但你的序言听起来像鼓声。:-)) 您的回答让我更深入地了解了inject 的酷炫之处。谢谢!【参考方案3】:

使用 ActiveSuport 的Hash#transform_values:

array.group_by |h| h[:type] .transform_values |hs| hs.map |h| h[:name]  
#=> "Meat"=>["one", "two"], "Fruit"=>["four"]

【讨论】:

transform_values 现在是 Ruby 本身的一部分(我认为是从 2.4 开始)。【参考方案4】:
array = [:type=>"Meat", :name=>"one", :type=>"Meat", :name=>"two", :type=>"Fruit", :name=>"four"]
array.inject() |memo, value| (memo[value[:type]] ||= []) << value[:name]; memo

【讨论】:

【参考方案5】:

我会这样做:

hsh =[:type=>"Meat", :name=>"one", :type=>"Meat", :name=>"two", :type=>"Fruit", :name=>"four"]
p Hash[hsh.group_by|h| h[:type] .map|k,v| [k,v.map|h|h[:name]]]

# >> "Meat"=>["one", "two"], "Fruit"=>["four"]

【讨论】:

【参考方案6】:

@ArupRakshit answer,稍作修改(为了清楚起见,在最后一个示例中添加了该函数):

def group(list, by, at)
  list.group_by  |h| h[by] .map  |k,v| [ k , v.map |h| h[at] ] .to_h
end

sample =[
  :type=>"Meat",  :name=>"one",  :size=>"big"   ,
  :type=>"Meat",  :name=>"two",  :size=>"small" ,
  :type=>"Fruit", :name=>"four", :size=>"small" 
]

group(sample, :type, :name) # => "Meat"=>["one", "two"], "Fruit"=>["four"]
group(sample, :size, :name) # => "big"=>["one"], "small"=>["two", "four"]

请注意,尽管问题中未提及,但您可能希望保留原来的 sample 原样。有些答案保留了这一点,有些则没有。

分组后 (list.group_by ...) 进行转换的部分(不修改原始样本的值)是:

.map  |k,v| [ k , v.map |h| h[at] ] .to_h

一些提示

    迭代组的Hash 对(第一个map),其中 对于每次迭代,我们都会收到|group_key, array] 并返回[group_key, new_array]Array(外部block), 最后to_hArray of Arrays 转换为Hash(此[[gk1,arr1],[gk2,arr2]...] 转换为此 gk1 =&gt; arr1, gk2 =&gt; arr2, ...

上面的步骤 (2) 中没有说明一个缺少的步骤。 new_array 是由 v.map |h| h[at] 制作的,它只转换了 array 的每个原始 Hash (h) 元素的值 at(所以我们从 Array of Hashes 移动到 Array 的元素)。

希望能帮助其他人理解这个例子。

【讨论】:

以上是关于Ruby 哈希数组。 group_by 并在一行中修改的主要内容,如果未能解决你的问题,请参考以下文章

如何按值按降序对哈希进行排序并在 ruby​​ 中输出哈希?

如何在哈希数组中获取特定键?

Ruby on Rails:以表单提交数组

Ruby中数组哈希的所有可能组合

Ruby数组与哈希测试中的堆栈级别太深

使用ruby在数组中过滤频繁项