上下文:尝试为db表中的每个created_at
天生成一个包含1个元素的数组。每个元素都是具有该created_at
天的记录的points
(整数)列的平均值。稍后将对此进行绘图,以显示每天的平均点数。
结果:我已经成功地做到了这一点,但要生成所需的结果,感觉需要不必要的代码量。
代码:
def daily_avg
# get all data for current user
records = current_user.rounds
# make array of long dates
long_date_array = records.pluck(:created_at)
# create array to store short dates
short_date_array = []
# remove time of day
long_date_array.each do |date|
short_date_array << date.strftime('%Y%m%d')
end
# remove duplicate dates
short_date_array.uniq!
# array of avg by date
array_of_avg_values = []
# iterate through each day
short_date_array.each do |date|
temp_array = []
# make array of records with this day
records.each do |record|
if date === record.created_at.strftime('%Y%m%d')
temp_array << record.audio_points
end
end
# calc avg by day and append to array_of_avg_values
array_of_avg_values << temp_array.inject(0.0) { |sum, el| sum + el } / temp_array.size
end
render json: array_of_avg_values
end
问题:我认为这是一个常见的extraction
问题,需要很多应用程序来解决,所以我想知道是否有一个已知的可重复模式来解决这样的问题?
还是一种更优化的方法来解决这个问题?
(我只是一个初级开发人员,所以如果您能提供任何建议,我们将不胜感激!)
是的,当你可以直接转到SQL时,这是很多不必要的东西(我假设你的应用程序中有一个名为Round
的类):
class Round
DAILY_AVERAGE_SELECT = "SELECT
DATE(rounds.created_at) AS day_date,
AVG(rounds.audio_points) AS audio_points
FROM rounds
WHERE rounds.user_id = ?
GROUP BY DATE(rounds.created_at)
"
def self.daily_average(user_id)
connection.select_all(sanitize_sql_array([DAILY_AVERAGE_SELECT, user_id]), "daily-average")
end
end
直接在数据库中执行此操作将比现在在ruby中执行更快(并且包含更少的代码)。
我建议你这样做:
grouped =
records.order(:created_at).group_by do |r|
r.created_at.strftime('%Y%m%d')
end
首先,在这里,您生成了与您希望在第一近似中获得的SQL接近的正确SQL,然后通过转换为仅日期的created_at
字段对结果记录进行分组。
points =
grouped.map do |(date, values)|
[ date, values.reduce(0.0, :audio_points) / values.size ]
end.to_h
# => { "1-1-1970" => 155.0, ... }
然后通过数组重新映射分组后的散列,用audio_points
计算平均值。
您可以使用group
和AR中内置的计算方法:http://guides.rubyonrails.org/active_record_querying.html#grouphttp://guides.rubyonrails.org/active_record_querying.html#calculations