在过去7天内迭代完成日期

时间:2016-05-16 05:22:37

标签: ruby-on-rails ruby date ruby-on-rails-4

我需要构建一个对象数组,以在图表上显示数据,该图表显示过去7天的数据。有时,数据库中将缺少7天记录中的某些记录,因此我需要显示记录并将值标记为0以在数组中包含7个对象。

现在我有

[
  {
    "date": "2016-05-14",
    "amount": 6000
  },
  {
    "date": "2016-05-12",
    "amount": 12000
  }
]

我想要的是

[
    {
        "date": "2016-05-14",
        "amount": 6000
    },
    {
        "date": "2016-05-13",
        "amount": 0
    },
    {
        "date": "2016-05-12",
        "amount": 12000
    },
    {
        "date": "2016-05-11",
        "amount": 0
    },
    {
        "date": "2016-05-10",
        "amount": 0
    },
    {
        "date": "2016-05-09",
        "amount": 0
    },
    {
        "date": "2016-05-09",
        "amount": 0
    }
]

5 个答案:

答案 0 :(得分:6)

ts = JSON.parse '[
{
  "date": "2016-05-14",
  "amount": 6000
},
{
  "date": "2016-05-12",
  "amount": 12000
}]' # support ruby < 2.2

((Date.today-6..Date.today).map do |d|
  [d.iso8601, {'amount' => 0, 'date' => d.iso8601 }]
end.to_h.merge ts.group_by { |e| e['date'] }).values.flatten

这里我们首先构建映射到零值的请求天数的哈希值,然后将其与现有值的哈希值合并。后者优先:

[Date.today].map do |d|
  [d.iso8601, {'amount' => 0, 'date' => d.iso8601 }]
end.to_h
#⇒ { '2016-05-16' => {'amount' => 0, 'date' => '2016-05-16' } }

(在真正的哈希中有七个项目。)

Enumerable#group_by产生相同的结构:

[ {'amount' => 42, 'date' => Date.today } ].group_by { |e| e['date'] }
#⇒ { '2016-05-16' => [{'amount' => 42, 'date' => '2016-05-16' }] }

作为最后一步,我们将后者合并到前者中,并通过获取结果的values并将其展平来摆脱日期键。

使用Hash#default_proc

hsh = Hash.new do |h, k|
  ts.detect do |e|
    e['date'] == k.iso8601
  end || { 'amount' => 0, 'date' => k.iso8601 }
end
(Date.today-6..Date.today).map { |d| hsh[d] }

答案 1 :(得分:2)

通过map reduce的功能方法可能是最简单和最有效的:

timeseries = [
  {
    "date":   "2016-05-14",
    "amount": 6000
  },
  {
    "date":   "2016-05-12",
    "amount": 12000
  }
]

last_7_days     = 6.days.ago.to_date..Date.today

# Transform array of hashes into a single hash with each key as a Date object.
#  Eg. { "2016-05-12" => 12000, "2016-05-14" => 6000 }
counts_each_day = timeseries.reduce(Hash.new(0)) do |acc, item|
  acc.merge(Date.parse(item[:date]) => item[:amount])
end

# Merge the results with an empty count, if it doesn't exist in our transformed hash.
filled_timeseries = last_7_days.map do |day|
  { date: day.to_s, amount: counts_each_day[day] }
end

<强>结果

[
  { :date => "2016-05-10", :amount => 0 },
  { :date => "2016-05-11", :amount => 0 },
  { :date => "2016-05-12", :amount => 12000 },
  { :date => "2016-05-13", :amount => 0 },
  { :date => "2016-05-14", :amount => 6000 },
  { :date => "2016-05-15", :amount => 0 },
  { :date => "2016-05-16", :amount => 0 }
]

答案 2 :(得分:1)

我希望这会有所帮助。

data = [
{
  "date": "2016-05-14",
  "amount": 6000
},
{
  "date": "2016-05-12",
  "amount": 12000
}] # Given data
dates = data.map do |datum| datum[:date] end # Extract dates from data.
today = Date.today # Get today.
dates_list = 7.times.map do |index| (today + index).strftime("%F") end # Get 7days from today.
dates_list.each do |date|
  next if dates.include? date # if data already includes the date, such as "2016-05-14", pass.
  data << {"date": date, "amount": 0} # if data does not include the date, append it with amount 0.
end

答案 3 :(得分:1)

这将得到正确答案:

require 'date'
require 'pp'

data = [
  {
    "date": "2016-05-14",
    "amount": 6000
  },
  {
    "date": "2016-05-12",
    "amount": 12000
  }
]

pp data

today = DateTime.now

7.times do |day|
  current_day = (DateTime.now - day).strftime("%F")
  next if data.find_index {|hash| hash[:date] == current_day }
  data.push({ "date": current_day, "amount": 0 })
end

pp data

这将产生以下输出:

[{:date=>"2016-05-14", :amount=>6000},
 {:date=>"2016-05-12", :amount=>12000},
 {:date=>"2016-05-15", :amount=>0},
 {:date=>"2016-05-13", :amount=>0},
 {:date=>"2016-05-11", :amount=>0},
 {:date=>"2016-05-10", :amount=>0},
 {:date=>"2016-05-09", :amount=>0}]

如果您希望按日期排序数组,可以使用:

data.sort {|a,b| a[:date] <=> b[:date] }.reverse

这将产生此输出:

[{:date=>"2016-05-15", :amount=>0},
 {:date=>"2016-05-14", :amount=>6000},
 {:date=>"2016-05-13", :amount=>0},
 {:date=>"2016-05-12", :amount=>12000},
 {:date=>"2016-05-11", :amount=>0},
 {:date=>"2016-05-10", :amount=>0},
 {:date=>"2016-05-09", :amount=>0}]

答案 4 :(得分:1)

这里我认为另一种方法相当清楚,并从数据而不是Date.today获取开始日期。它假定输入中没有无效日期,并且输入不为空。

请注意,通过指定class test24{ public static void main ( String [] args ) { int[] arr = {6,3,9,0}; test24 test = new test24(); System.out.println(Arrays.deepToString(test.repmat(arr,2,1,2))); } public static int[][][] repmat (int[] array, int rows, int columns, int depth) { int arrayColumns = array.length; int resultColumns = arrayColumns * columns; int[][][] result = new int[rows][resultColumns][depth]; int z = 0; for (int d = 0; d < depth; d++) { for (int r = 0; r < rows; r++) { for (int c = 0; c < resultColumns; c++) { result[r][c][d] = array[z++]; if (z >= arrayColumns) { z = 0; } } } } return result; } } 某些内容,您可以使用与"string":...相同的内容,但需要采用更加混乱的方式。我想这是来自JSON数据,但它应该被解析为一个Ruby数组的哈希。我在我的代码中对此进行了更改。

string:...

顺便说一句,这可以在没有Rails的Ruby中运行。此外,代码发布在https://gist.github.com/keithrbennett/fd876aac938f1e5d6222896dbd30e8f2