Ruby:转换数组中的对象后传递键/值

时间:2018-07-13 17:35:55

标签: ruby-on-rails 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"}
]

将由其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"}
]

5 个答案:

答案 0 :(得分:4)

由于: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) }

答案 1 :(得分:2)

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

::ng-deep .mat-progress-bar[dir='rtl'],
:host-context([dir='rtl']) ::ng-deep .mat-progress-bar {
  transform: none;
}

答案 2 :(得分:2)

我认为这对于reduce是一个很好的用例,因为在分组之后,您需要首先从[ID, VALUES]除去结果group_by数组中的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之后的数组大小不会改变,因此{{1 }}就足够了。

但是在重写代码时,我在想用所有键创建一个新的散列并从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

在线演示here

答案 3 :(得分:1)

即使没有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"}]

答案 4 :(得分:-1)

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" } }