根据对象的属性获取activerecord对象数组的唯一元素的最佳方法是什么?

时间:2010-01-14 00:45:42

标签: ruby-on-rails arrays activerecord

在我的应用程序中,我试图仅根据loss_report的两个属性显示activerecord对象数组(loss_reports)的唯一元素。

架构

class Agent < ActiveRecord::Base
  has_many :loss_reports, :through => policy  
end

class LossReport < ActiveRecord::Base
  belongs_to :agent  
end

我首先尝试覆盖eql?和LossReport的哈希方法,以便我可以做类似的事情:

选项1:

class LossReport ...
  def eql? other
    self.policy_id == other.policy_id && loss_occurred_on.hash == self.loss_occurred_on  
  end 

  def hash 
    policy_id + loss_occurred_on.hash
  end
end  

class Agent ...
  def unique_losses
    loss_reports.to_set
  end
end

但很快删除了代码,因为ActiveRecord已经覆盖了这些方法,而且我不确定是否会产生影响。

选项2:

class Agent ...
  def unique_losses
    loss_reports.sort{|l1,l2| l2.created_at <=> l1.created_at}.group_by{|l| (l.policy_id + l.loss_occurred_on.hash)}.collect{|hl| hl[-1].first}
  end
end  

选项3:

 class Agent
   def unique_losses
     hsh_array = []
     unique = []
     loss_reports.sort{|l1,l2| l2.created_at <=> l1.created_at}.each do |l|
       unique << l unless hsh_array.include?(l.hsh)
       hsh_array << l.hsh
     end
     unique         
   end
 end

基准测试结果:

Benchmark.bmbm do |bm|
  bm.report("option 2") do
    losses.sort{|l1,l2| l2.created_at <=> l1.created_at}.group_by{|l| (l.policy_id +  l.loss_occurred_on.hash)}.collect{|hl| hl[-1].first}
  end
  bm.report("option 3") do
    hsh_array,unique = [],[]
    losses.sort{|l1,l2| l2.created_at <=> l1.created_at}.each do |l|
      unique << l unless hsh_array.include?(l.policy_id+l.loss_occurred_on.hash)
      hsh_array << l.policy_id + l.loss_occurred_on.hash
    end
  end
end
Rehearsal --------------------------------------------
option 2   0.400000   0.000000   0.400000 (  0.407615)
option 3   0.250000   0.000000   0.250000 (  0.254399)
----------------------------------- total: 0.650000sec

               user     system      total        real
option 2   0.400000   0.000000   0.400000 (  0.403535)
option 3   0.250000   0.000000   0.250000 (  0.262578)

两种方式都不对,但两者都有效。哪个是最好的选择还是有更好的方法?

1 个答案:

答案 0 :(得分:1)

我不知道基准测试,但似乎inject是最简单的方法:

loss_reports.inject([]) do |arr, report|
  arr << report unless arr.detect{|r| ... } 
end

或者更好的方法是用...定义带有自定义SQL组的named_scope。