Ruby:在转换数组中的对象后传递键/值
鉴于数据:
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"} ]
得到的内容由他们的sort
和ids
连接:
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
但想知道如何在formatted
变量中包含foo
,以便我可以映射它以获得所需的输出或者是否可能?
期望的输出:
[ {"id":14, "concated_value":"9105", foo: "2022"}, {"id":15, "concated_value":"3134", foo: "2888"}, {"id":16, "concated_value":"8449", foo: "3112"} ]
因为: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)
- 然后我们按排序排序并同时删除它。
.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) }
tap {|h| h[:concated_value] = h.delete(:content) }
我们可以使用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
我认为这是reduce
一个很好的用例,因为在分组后你需要首先从group_by
删除结果[ID, VALUES]
数组中的[ID, VALUES]
然后返回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"}]
在线演示在这里
即使没有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"}]
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 (aka merge!
)的forms,它使用一个块来确定两个哈希中合并的键的值(这里是:id
的值)。 有关三个块变量(此处为_
, o
和n
)的说明,请参阅doc。
注意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" } }