高效的数据转换

时间:2017-02-02 20:41:30

标签: arrays ruby

我有这样的数据格式,数组a

[{
  agreement_id: 1,
  app_user_id: 1,
  agency_name: 'Small business 1'
},
{
  agreement_id: 1,
  app_user_id: 2,
  agency_name: 'Small business 1'
},
{
  agreement_id: 2,
  app_user_id: 1,
  agency_name: 'Small business 2'
}]

我显然简化了数组元素,实际上我有列表或AR对象,但它的要点应该是相同的。

我基本上希望创建一个用户协议地图。我的意思是这个输出:

    [{
      agreement_id: 1,
      app_users: [1, 2],
      agency_name: 'Small business 1'
    },
    {
      agreement_id: 2,
      app_user_id: [1],
      agency_name: 'Small business 2'
    }]

这就是我想要的方式(伪代码):

  • 实例化ruby Hash h
  • 创建数组b
  • 迭代数组a
  • 对于数组的每个元素(elem),检查h是否没有elem[:agreement_id]键,然后在名为elem的{​​{1}}上创建新属性并将app_users作为值放在新属性中(一个值的数组)。

然后将没有[elem[: app_user_id]]属性的elem推送到数组b。

然后我们迭代下一个元素app_user_id,如果它没有elem键,我们就会做同样的事情。

但是,如果elem[:agreement_id]具有h键,请将elem[:agreement_id]附加到elem[: app_user_id]属性数组。然后将没有app_users属性的elem推送到数组b。

是否有更简便的方法来完成同样的事情,更多内联现有的ruby方法?

3 个答案:

答案 0 :(得分:3)

group_byvalues_atmap混合可以让您更接近目标。

此代码组将agreement_idagency_name的值具有相同的哈希值。

即使数组包含一个元素,并且第一个:app_user_ids已重命名为customer_name,输出键始终为agency_name

data = [{
  agreement_id: 1,
  app_user_id: 1,
  agency_name: 'Small business 1'
},
{
  agreement_id: 1,
  app_user_id: 2,
  agency_name: 'Small business 1'
},
{
  agreement_id: 2,
  app_user_id: 1,
  agency_name: 'Small business 2'
}]

puts data.group_by{ |hash|
  hash.values_at(:agreement_id, :agency_name)
}.map{ |(agreement_id, agency_name), hashes|
  {
    agreement_id: agreement_id,
    agency_name: agency_name,
    app_user_ids: hashes.map { |h| h[:app_user_id] }
  }
}
# {:agreement_id=>1, :agency_name=>"Small business 1", :app_user_ids=>[1, 2]}
# {:agreement_id=>2, :agency_name=>"Small business 2", :app_user_ids=>[1]}

答案 1 :(得分:0)

我假设(与其他人一样)OP示例中的键:customer_name应为:agency_name,并且:agency_name的值对于具有相同值的所有哈希值都相同:agreement_id

h = [{
  agreement_id: 1,
  app_user_id: 1,
  agency_name: 'Small business 1'
},
{
  agreement_id: 1,
  app_user_id: 2,
  agency_name: 'Small business 1'
},
{
  agreement_id: 2,
  app_user_id: 1,
  agency_name: 'Small business 2'
}]

h.each_with_object({}) do |g,h|
  h.update(g[:agreement_id]=>{ agreement_id: g[:agreement_id],
                               app_user_id:   [g[:app_user_id]],
                               agency_name:   g[:agency_name]
                             }
          ) do |_,o,n| { agreement_id: o[:agreement_id],
                         app_user_id:  o[:app_user_id] + n[:app_user_id],
                         agency_name:    o[:agency_name]
                       }
            end
end.values
  #=> [{:agreement_id=>1, :app_user_id=>[1, 2], :agency_name=>"Small business 1"},
  #    {:agreement_id=>2, :app_user_id=>[1], :agency_name=>"Small business 2"}] 

这使用Hash#update(aka merge!)的形式,它使用一个块来计算两个哈希值中合并的键值。有关进一步说明,请参阅文档,特别是每个块的三个键的内容。 (我使用下划线表示第一个键 - 公共键 - 表示它没有在块计算中使用。)

答案 2 :(得分:0)

谢谢大家的回答,我同时做到了这一点,我没有看到回复。这是我的解决方案:

json_map = {}

[{
  agreement_id: 1,
  app_user_id: 1,
  agency_name: 'Small business 1'
},
{
  agreement_id: 1,
  app_user_id: 2,
  agency_name: 'Small business 1'
},
{
  agreement_id: 2,
  app_user_id: 1,
  agency_name: 'Small business 2'
}].each do |ag_deal|
  app_user_id = ag_deal[:app_user_id]
  agreement_id = ag_deal[:agreement_id]

  if json_map.key?(agreement_id)
    ag_deal = json_map[agreement_id]
    ag_deal[:app_user_ids] << app_user_id
  else
    ag_deal[:app_user_ids] = [app_user_id]
    json_map[agreement_id] = ag_deal.except(:app_user_id)
  end
end

所以期望的结果是json_map.values,粘贴:

[
  {:agreement_id=>1, :agency_name=>"Small business 1", :app_user_ids=>[1, 2]},
  {:agreement_id=>2, :agency_name=>"Small business 2", :app_user_ids=>[1]}
]

我将对这些进行基准测试并使用最快的一个,我将在数组中创建一个包含几个随机用户的1000个项目,希望能够正确模拟它。

以下是我的基准测试结果:

=======Cary ======
user     system      total        real
0.010000   0.000000   0.010000 (  0.008853)

=====Eric====
user     system      total        real
0.020000   0.010000   0.030000 (  0.017856) 

====me=====
user     system      total        real
0.010000   0.030000   0.040000 (  0.020826) 

这是样本数据的生成方式:

users = [1, 2, 3, 4, 5]
data = []
1000.times do |i|
  data << {
    agreement_id: i,
    app_user_id: users.shuffle.first,
    agency_name: "Small business #{i}"
  }
end