2016-05-16 95 views
1

我需要构建一个对象数组,以在显示过去7天数据的图上显示数据。有时7天的某些记录将在数据库中不存在,因此我需要显示记录并将值标记为0以使数组中有7个对象。过去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 
    } 
] 
+0

你能否告诉我们,是日期字段,你的表? –

+0

@PraveshKhatri'paid_on'是的,这是基本上,因为有这些日期的数据被提取的记录。对于天不需要设置默认值 –

回答

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] } 
+0

该死的光滑:) –

+0

请你分享一下解释,所以我可以学习 –

+0

@HarshaMV放弃了一些解释。 – mudasobwa

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 
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}] 
2

的功能办法通过地图降低可能是最简单,最有效的:

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 } 
] 
+0

'没有将日期隐式转换为字符串“出现以下错误 –

+0

在哪一行是错误? –

+0

'acc.merge(Date.parse(item [:date])=> item [:amount])'该行 –

1

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

请注意,通过指定"string":...您所说的与string:...相同的东西,但方式更加混乱。我想这是来自JSON数据,但它应该被解析成散列的Ruby数组。我在我的代码中改变了这个。

#!/usr/bin/env ruby 

require 'date' 


def to_date(string) 
    Date.strptime(string, '%Y-%m-%d') 
end 


# Transform the input to a hash whose keys are date objects 
# and whose values are the original input records 
def transform_input_to_hash(input_records) 
    input_records.each_with_object({}) do |record, hsh| 
    key = to_date(record[:date]) 
    hsh[key] = record 
    end 
end 


def fill_in_missing_dates(input_records) 
    input_record_hash = transform_input_to_hash(input_records) 

    start_date = input_record_hash.keys.first 
    output_dates = ((start_date - 6)..start_date).to_a.reverse 

    output_dates.each_with_object([]) do |date, array| 
    array << if input_record_hash.keys.include?(date) 
     input_record_hash[date] 
    else 
     { date: date, amount: 0 } 
    end 
    end 
end 



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



output_array = fill_in_missing_dates(INPUT_RECORDS) 
puts output_array 

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