【问题标题】:Ruby: Passing down key/value after transforming objects in arrayRuby:在数组中转换对象后传递键/值
【发布时间】:2018-07-14 06:34:23
【问题描述】:

给定数据:

data = [
 {"id":14, "sort":1, "content":"9", foo: "2022"},
 {"id":14, "sort":4, "content":"5", foo: "2022"},
 {"id":14, "sort":2, "content":"1", foo: "2022"},
 {"id":14, "sort":3, "content":"0", foo: "2022"},
 {"id":15, "sort":4, "content":"4", foo: "2888"},
 {"id":15, "sort":2, "content":"1", foo: "2888"},
 {"id":15, "sort":1, "content":"3", foo: "2888"},
 {"id":15, "sort":3, "content":"3", foo: "2888"},
 {"id":16, "sort":1, "content":"8", foo: "3112"},
 {"id":16, "sort":3, "content":"4", foo: "3112"},
 {"id":16, "sort":2, "content":"4", foo: "3112"},
 {"id":16, "sort":4, "content":"9", foo: "3112"}
]

得到由sortids 连接的内容:

formatted = data.group_by { |d| d[:id]}.transform_values do |value_array|
  value_array.sort_by { |b| b[:sort] }
             .map     { |c| c[:content] }.join
end

puts formatted
#=> {14=>"9105", 15=>"3134", 16=>"8449"}

我知道foo 存在于value_array 中,但我想知道如何将foo 包含在formatted 变量中,这样我就可以通过它进行映射以获得所需的输出或者是否有可能?

所需的输出:

[
 {"id":14, "concated_value":"9105", foo: "2022"},
 {"id":15, "concated_value":"3134", foo: "2888"},
 {"id":16, "concated_value":"8449", foo: "3112"}
]

【问题讨论】:

    标签: ruby-on-rails ruby


    【解决方案1】:

    因为:foo:id 独有的。你可以这样做:

    data.group_by {|h| h[:id]}.map do |_,sa| 
      sa.map(&:dup).sort_by {|h| h.delete(:sort) }.reduce do |m,h| 
         m.merge(h) {|key,old,new| key == :content ? old + new : old } 
      end.tap {|h| h[:concated_value] = h.delete(:content) }
    end  
    #=> [
    # {"id":14, foo: "2022", "concated_value":"9105"},
    # {"id":15, foo: "2888", "concated_value":"3134"},
    # {"id":16, foo: "3112", "concated_value":"8449"}
    # ]
    
    • 首先我们按 id 分组。 group_by {|h| h[:id]}
    • 然后我们复制组中的哈希值(以免破坏原始值)。 map(&:dup)
    • 然后我们按so​​rt排序,同时删除。 .sort_by {|h| h.delete(:sort) }
    • 然后我们将这些组合并在一起并仅连接内容键。 m.merge(h) {|key,old,new| key == :content ? old + new : old }
    • 然后我们只需将content 的密钥更改为concated_value tap {|h| h[:concated_value] = h.delete(:content) }

    【讨论】:

    • 非常感谢您的详尽解释!只是一个额外的问题。假设我想在来自:content 的某些项目上使用reject,其中.length > 1。在这种情况下会起作用吗?
    • 当然只是拒绝他们来自data.reject {|h| h[:content].length > 1 }.group_by#...
    【解决方案2】:

    我们可以使用 value_array 中的第一个值来获取我们的 :id 和 :foo 值

    formatted = data.group_by { |d| d[:id]}.values.map do |value_array|
      concated_value = value_array.sort_by { |b| b[:sort] }
                                  .map     { |c| c[:content] }.join
      value_array.first.slice(:id, :foo)
                 .merge concated_value: concated_value
    end
    

    【讨论】:

      【解决方案3】:

      我认为这是reduce 的一个很好的用例,因为在分组后,您首先需要从group_by 中删除生成的[ID, VALUES] 数组中的ID,然后只返回VALUES 部分的简化版本- 这一切都可以在没有任何 ActiveSupport 等依赖项的情况下完成:

      data
        .group_by{ |d| d[:id] } # Get an array of [ID, [VALUES]]
        .reduce([]) do |a, v| # Reduce it into a new empty array
          # Append a new hash to the new array
          a << {
            id: v[1].first[:id], # Just take the ID of the first entry
            foo: v[1].first[:foo], # Dito for foo
            concatenated: v[1] 
              .sort_by{ |s| s[:sort] } # now sort all hashes by its sort key
              .collect{ |s| s[:content] } # collect the content
              .join # and merge it into a string
          }
        end
      

      输出:

      [{:id=>14, :foo=>"2022", :concatenated=>"9105"}, 
       {:id=>15, :foo=>"2888", :concatenated=>"3134"}, 
       {:id=>16, :foo=>"3112", :concatenated=>"8449"}]
      

      编辑

      当我开始编写之前的解决方案时,我想到了一些其他方法,reduce 并不是真正需要的,因为group_by 之后的数组大小不会改变,所以map 就足够了。

      但是在重写代码时,我在想用所有键创建一个新的散列并从VALUES 中的第一个散列中复制所有值有点太多的工作,所以拒绝它会更容易开销键:

      keys_to_ignore = [:sort, :content]
      
      data
        .group_by{ |d| d[:id] } # Get an array of [ID, [VALUES]]
        .map do |v| 
          v[1]
            .first # Take the first hash from [VALUES]
            .merge({'concatenated': v[1] # Insert the concatenated values
              .sort_by{ |s| s[:sort] } # now sort all hashes by its sort key
              .collect{ |s| s[:content] } # collect the content
              .join # and merge it into a string
            })
            .select { |k, _| !keys_to_ignore.include? k }
        end
      

      输出

      [{:id=>14, :foo=>"2022", :concatenated=>"9105"}, 
       {:id=>15, :foo=>"2888", :concatenated=>"3134"}, 
       {:id=>16, :foo=>"3112", :concatenated=>"8449"}]
      

      在线演示here

      【讨论】:

      • 这个超级干净,喜欢。谢谢你告诉我collect
      • reduce 是不必要的,map 就足够了 - 我还添加了另一种方法来解决这个问题,以防从原始哈希返回更多值。跨度>
      【解决方案4】:

      即使没有 Rails 也可以使用:

      $irb> formatted = []
      $irb> data.sort_by!{|a| a[:sort]}.map {|z| z[:id]}.uniq.each_with_index { |id, index| formatted << {id: id, concated_value: data.map{|c| (c[:id] == id ? c[:content] : nil)}.join, foo: data[index][:foo]}}
      $irb> formatted
      [{:id=>14, :concated_value=>"9105", :foo=>"2022"}, 
      {:id=>15, :concated_value=>"3134", :foo=>"2888"},
      {:id=>16, :concated_value=>"8449", :foo=>"3112"}]
      

      【讨论】:

        【解决方案5】:
        data.sort_by { |h| h[:sort] }.
             each_with_object({}) do |g,h| h.update(g[:id]=>{ id: g[:id],
               concatenated_value: g[:content].to_s, foo: g[:foo] }) {  |_,o,n|
                 o.merge(concatenated_value: o[:concatenated_value]+n[:concatenated_value]) }
             end.values
          #=> [{:id=>14, :concatenated_value=>"9105", :foo=>"2022"},
          #    {:id=>15, :concatenated_value=>"3134", :foo=>"2888"},
          #    {:id=>16, :concatenated_value=>"8449", :foo=>"3112"}]
        

        这使用Hash#update(又名merge!)的形式,它使用一个块来确定在被合并的两个哈希中存在的键的值(这里是:id的值)。三个块变量的描述见文档(这里是_on)。

        注意values(末尾)的接收者如下。

        { 14=>{ :id=>14, :concatenated_value=>"9105", :foo=>"2022" },
          15=>{ :id=>15, :concatenated_value=>"3134", :foo=>"2888" },
          16=>{ :id=>16, :concatenated_value=>"8449", :foo=>"3112" } } 
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2015-07-23
          • 1970-01-01
          • 1970-01-01
          • 2016-02-05
          • 2018-09-11
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多