填写分组查询的空白日期,返回一些空行

时间:2015-11-04 14:49:28

标签: mysql ruby-on-rails ruby

我试图在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 

那么,如何正确创建空值行的日期?然后我怎样才能确保它们符合正确的顺序?就像我说的那样,我对此很满意。算法不是我的一杯茶。

2 个答案:

答案 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插件所需要的