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_h
将Array of Array
s 转换为Hash
(此[[gk1,arr1],[gk2,arr2]...]
转换为此 gk1 => arr1, gk2 => arr2, ...
)
上面的步骤 (2) 中没有说明一个缺少的步骤。 new_array
是由 v.map |h| h[at]
制作的,它只转换了 array
的每个原始 Hash
(h
) 元素的值 at
(所以我们从 Array of Hash
es 移动到 Array
的元素)。
希望能帮助其他人理解这个例子。
【讨论】:
以上是关于Ruby 哈希数组。 group_by 并在一行中修改的主要内容,如果未能解决你的问题,请参考以下文章