【问题标题】:Ruby array of hash. group_by and modify in one lineRuby 哈希数组。 group_by 并在一行中修改
【发布时间】: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

【问题讨论】:

标签: ruby-on-rails ruby hash enumerable


【解决方案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
      

      一些提示

      1. 迭代组的Hash 对(第一个map),其中
      2. 对于每次迭代,我们都会收到|group_key, array] 并返回[group_key, new_array]Array(外部block),
      3. 最后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 的元素)。

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-08-16
        • 1970-01-01
        • 2012-04-12
        • 1970-01-01
        • 2011-11-19
        • 1970-01-01
        • 2020-10-09
        相关资源
        最近更新 更多