2014-11-20 22 views
1

我正在试图提高代码性能的方法中使用这些模型和以下行。Ruby - 如何加快循环遍历“.each”数组?

class Location < ActiveRecord::Base 
    belongs_to :company 
end 
class Company < ActiveRecord::Base 
    has_many :locations 
end 

在该方法中:

locations_company = [] 

### 
found_locations = Location.within(distance, origin: from_result.split(',')).order("distance ASC") 
### 0.002659s 

### 
found_locations.each do |location| 
    locations_company << location.company 
end 
### 45.972285s 

### 
companies = locations_company.uniq{|x| x.id} 
### 0.033029s 

的代码具有此功能 - 第一,抢指定半径内的所有位置。然后,从每个找到的行取公司并将其保存到准备好的数组中。这是有问题的部分 - 每个循环需要45秒来处理。

然后从这个新创建的数组中删除重复项。

我仍然想知道是否会有更好的方法来解决这种情况,但我恐怕我现在没有看到它,所以我想问问你们,我可以如何加快循环与.each将数据保存到数组 - 是否有更好的方法在ruby中从对象中获取一些信息?

非常感谢您的时间,我一整天都沉浸在这个问题中,但仍然没有更有效的解决方案。

+0

如果你看'found_locations',你会注意到它可能是一个查询代理,而不是一个合并的结果集。 '#each'几乎肯定不是你的瓶颈;你应该正确地分析你的代码来找到瓶颈。 – 2014-11-20 19:54:13

+0

这个问题似乎是脱离主题,因为它是关于重构和提高现有代码的性能,应该在[codereview.se]上。 – 2014-11-20 22:25:44

回答

6

最好的方法是不循环。您的最终目标似乎是找到指定区域内的所有公司。

found_locations = Location.within(distance, origin: from_result.split(',')).order("distance ASC") 
companies = Company.where(id: found_locations.pluck(:company_id).uniq) 
+0

如果db支持''Company.distinct''而不是'bleh.uniq'可能会有帮助。 – nicooga 2014-11-20 20:36:03

+0

Company.distinct不是必需的。如果你离开uniq,它只是将一个更大的数组传递给'WHERE id IN []'查询。即使数组中包含多次ids,数据库也只会为每个公司返回一条记录。我个人不喜欢向查询中发送更多的信息,并且添加/删除'uniq'不会对性能产生实质性影响。 – 2014-11-20 20:43:21

+0

取决于'found_locations'是否实际上将被用于公司之外,您可能会对此采取不同的变化。如果你打算单独使用'found_locations',那么你可以/应该强制它使用'to_a'到数组,然后将下一行的逻辑改为'Company.where(id:found_locations.map(&:id) .uniq)'。如果你不打算单独使用found_locations,那么我放在那里的将是最好的,因为那样你甚至不会创建'Location'对象,而只需要拉你需要的id。 – 2014-11-20 20:50:05

1

我认为,需要所有的时间的事情是不是each,而查询到的分贝。

第一行,虽然它构建查询并不真正运行它。

我相信,如果你写的代码如下:

locations_company = [] 

found_locations = Location.within(distance, origin: from_result.split(',')).order("distance ASC") 

### this line will take most of the time 
found_locations = found_locations.to_a 
###  

### 
found_locations.each do |location| 
    locations_company << location.company_id 
end 
### 

### 
companies = locations_company.uniq{|x| x.id} 
### 

你会看到each将采取少了很多时间。您应该考虑优化查询。


由于@AlexPeachey已经下面评论,也location.company将涉及对列表中的每个位置的查询,因为它是一个关系。你可能想通过增加急切地加载公司:

found_locations = Location.includes(:company).within(distance, origin: from_result.split(',')).order("distance ASC") 
+0

查询可能会很慢,但是每种方法都不会立即使用此方法,因为您每次都通过每个循环对公司表执行查询。为了避免这种情况,更改为'Location.includes(:company)'将只加载一个额外查询所需的所有公司。 – 2014-11-20 20:47:26

+0

谢谢@AlexPeachey,我错过了那部分。更新了答案 – 2014-11-20 20:57:10

1

的问题是不是在每一个,但在查询中只有开始执行,当你开始遍历它。 found_locations不是查询的结果,它是一个查询生成器,它将在需要时执行查询(例如,当您开始迭代结果时)。