我试图在n天的过程中获取统计数据。我并不真正关心输出格式,只要它按顺序显示日期,并将值显示为数组... all_data查询(下面)将获得确定时间段内的所有值,但它赢了“为没有值的日期创建行。目标是能够输出:
# Get 10 (for example) days of data->
[
{date:'datetime-day-ago-10', values: [{foo:'bar', foo2:'bar2', foo3:'bar'3}]}
{date:'datetime-day-ago-9', values: [{foo:'bar', foo2:'bar2', foo3:'bar'3}]}
{date:'datetime-day-ago-8', values: []}
{date:'datetime-day-ago-7', values: [{foo:'bar', foo2:'bar2', foo3:'bar'3}]}
{date:'datetime-day-ago-6', values: []}
{date:'datetime-day-ago-5', values: []}
{date:'datetime-day-ago-4', values: [{foo:'bar', foo2:'bar2', foo3:'bar'3}]}
{date:'datetime-day-ago-3', values: [{foo:'bar', foo2:'bar2', foo3:'bar'3}]}
{date:'datetime-day-ago-2', values: []}
{date:'datetime-day-ago-1', values: []}
]
这是我现在使用的方法。
def counts
from = params[:days].to_i.days.ago.to_time.beginning_of_day
to = 0.days.ago.to_time.end_of_day
all_data = current_user.values.all
.where(field_id: params[:field_id], created_at: from .. to)
.where.not(input: nil)
.order(input: :desc)
.group_by { |value| value['created_at'].to_date}
.map {|k,v| [k,v]}
grouped = (0..params[:days].to_i-1).to_a.inject([]) do |memo, i|
if all_data[i].present?
date = all_data[i][0].to_time
values = all_data[i][1].group_by { |value| value.input }.map { |k, v| {input:k, count:v.length} }.sort! { |x,y| x['input'] <=> y['input']}
total = 0
values.each do |value|
total = total + value[:count]
end
else
# This is where I can't figure out how to assign a date for rows which have no data.
date = ((i-params[:days].to_i+1)*-(1)).days.ago.beginning_of_day
end
memo << {'date': date, 'total': total || 0, 'values': values || []}
memo
end
grouped = grouped.sort_by { |day| day['date'] }
render json: grouped
end
# Sample of entire output for 10 days
0: {date: "2015-10-31T00:00:00.000-07:00", total: 15,…} <- 10-31-2015 HAS VALUES
1: {date: "2015-11-01T00:00:00.000-07:00", total: 49,…} <- 11-01-2015 HAS VALUES
2: {date: "2015-11-02T00:00:00.000-08:00", total: 10,…} <- 11-02-2015 HAS VALUES
3: {date: "2015-11-03T00:00:00.000-08:00", total: 21,…} <- 11-03-2015 HAS VALUES
4: {date: "2015-10-30T00:00:00.000-07:00", total: 0, values: []}
5: {date: "2015-10-31T00:00:00.000-07:00", total: 0, values: []} \
6: {date: "2015-11-01T00:00:00.000-07:00", total: 0, values: []} - No matter what I try I can't get these dates to be the 5 days before 10-31-2015...
7: {date: "2015-11-02T00:00:00.000-08:00", total: 0, values: []} /
8: {date: "2015-11-03T00:00:00.000-08:00", total: 0, values: []}
9: {date: "2015-11-04T00:00:00.000-08:00", total: 0, values: []} <- 11-04-2015 This should appear up there ^ under the last HAS VALUES row
那么,如何正确创建空值行的日期?然后我怎样才能确保它们符合正确的顺序?就像我说的那样,我对此很满意。算法不是我的一杯茶。
答案 0 :(得分:0)
可能,你可以来自这方面:
grouped = (from.to_date..to.to_date).each do |dt|
existing_data = all_data.detect{ |data| data.first == dt }
if existing_data
# you code for existing date
else
# you code for non-existing date
end
# ...
end
# And, I think, it is not required in this case
#grouped = grouped.sort_by { |day| day['date'] }
答案 1 :(得分:0)
这可能会在某些方面有所不同,但这里是我如何设法获得所需的输出。我决定让这个rails控制器为我提供我的Angular Google Charts所需的确切输入,但我明确了问题直接回答的地方在填写缺失日期和缺少行值的方法方面:
from = params[:from] || (params[:days].to_i-1).days.ago.to_date
to = params[:to] || 0.days.ago.to_date
all_data = current_user.values.all
.where(field_id: params[:field_id], created_at: from.beginning_of_day .. to.end_of_day)
.where.not(input: nil)
.order(input: :desc)
.group_by { |value| [value['created_at'].to_date, value['input']] }
.map { |k, v| {'date': k.first, 'input': k.last, 'count': v.length} }
# the object which will hold the data to be returned
grouped = {:cols => [], :rows => [] }
# Populate columns with a determined set of values for inputs
diffvals = Array.new
all_data.each { |day| diffvals.push day[:input] }
diffvals = diffvals.uniq
diffvals.each { |input| grouped[:cols].push({'id': "#{input}-id", 'label': input, 'type': 'number'}) }
grouped[:cols].unshift({'id': 'day', 'label': 'Date', 'type': 'string'})
##
## This and the next commented section answer the question:
##
# Create a set of dates that the actual data will be merged into
zeros = Array.new
(0..params[:days].to_i-1).each do |n|
diffvals.each do |input|
hash = Hash.new
hash[:date], hash[:input], hash[:count] = n.days.ago.to_date, input, 0
zeros.push hash
end
end
##
##
##
# Group the data by date after mapping the actual data onto the premade set of dates
data_by_date = zeros.map do |first_hash|
all_data.each do |second_hash|
if first_hash[:date] == second_hash[:date] && first_hash[:input] == second_hash[:input]
first_hash[:count] = second_hash[:count]
break
end
end
first_hash
end.group_by { |i| i[:date]}
# Populate rows of data
data_by_date.each_with_index do |(date, values), day|
#
# The first column is the date
grouped[:rows][day] = {c: [{ v: date }] }
#
# Then columns of value inputs
(0..grouped[:cols].length-2).each { |value| grouped[:rows][day][:c].push({v: values[value][:count] }) }
#
# Then a column for the total of all value counts
grouped[:rows][day][:c].push({v: values.map {|v| v[:count]}.reduce(0, :+) })
end
grouped[:cols].push({'id': "s", 'label': "Total", 'type': 'number'})
render json: grouped
这是一组示例数据。喜欢的颜色,3种可能的颜色选择。超过5天:
cols:
0: {id: "day", label: "Date", type: "string"}
1: {id: "Red-id", label: "Red", type: "number"}
2: {id: "Green-id", label: "Green", type: "number"}
3: {id: "Blue-id", label: "Blue", type: "number"}
4: {id: "s", label: "Total", type: "number"}
rows:
0: {c: [{v: "2015-11-01"}, {v: 18}, {v: 23}, {v: 8}, {v: 49}]}
1: {c: [{v: "2015-11-02"}, {v: 4}, {v: 4}, {v: 2}, {v: 10}]}
2: {c: [{v: "2015-11-03"}, {v: 5}, {v: 16}, {v: 0}, {v: 21}]}
3: {c: [{v: "2015-11-04"}, {v: 0}, {v: 0}, {v: 0}, {v: 0}]}
4: {c: [{v: "2015-11-05"}, {v: 0}, {v: 1}, {v: 6}, {v: 7}]}
这正是Angular Google Chart插件所需要的