我們有一個日期間隔陣列:
[[start_date, end_date], [], ...]
例如:
[
[01.02.2020, 01.05.2020], # 3 months
[01.01.2020, 01.10.2020], # 9 month, but we cant count the time already in the array, so 6 month
...
]
作為執行演算法的結果,我們應該得到周期持續時間的天數/月數/年數。我怎么寫這個?我會感謝你的幫助
uj5u.com熱心網友回復:
對于天數,任務可以很容易地解決:
- 將輸入轉換為一組日期的陣列(通過
Range) - 計算集合并集
所以:
input = [["01.02.2020", "01.05.2020"],["01.01.2020", "01.10.2020"]]
days =
input
.map { |start_date, end_date| Date.parse(start_date)..Date.parse(end_date) }
.map(&:to_set)
.reduce(&:union)
(可以更簡潔地完成;把這個留給你)
這為您提供了準確的天數 ( days.size),而不會在范圍的交叉點中重復出現。但是要達到月/年有點棘手。好的,本身可能并不棘手,但需要對如何計算部分覆寫的月/年進行一些額外的說明(例如,是否可以將 8 月的最后 2 周和 9 月的前 2 周計算為 1 個月)。對于最簡單的情況,它可能就像將天除以 30 得到月和除以 360 得到年一樣簡單(但規則可能更復雜)。
uj5u.com熱心網友回復:
我會按照這個一般順序做一些事情:
- 將每個日期轉換為
time_t. - 對這些進行排序。
- 抓住第一個和最后一個(最早和最近的)。
- 減去以獲得差異。
- 將結果轉換回天/月/年。
uj5u.com熱心網友回復:
假設我們有以下范圍。
ranges = ['01.02.2020'..'04.05.2020', '18.07.2020'..'12.08.2020',
'14.09.2020'..'29.10.2020', '21.03.2020'..'21.06.2020',
'07.02.2020'..'14.06.2020', '02.07.2020'..'12.07.2020']
第一步是將這些轉換為日期物件的范圍。
require 'date'
arr = ranges.map { |r| Date.parse(r.begin)..Date.parse(r.end) }
#=> [#<Date: 2020-02-01 ((2458881j,0s,0n), 0s,2299161j)>..#<Date: 2020-05-04 ((2458974j,0s,0n), 0s,2299161j)>,
# #<Date: 2020-07-18 ((2459049j,0s,0n), 0s,2299161j)>..#<Date: 2020-08-12 ((2459074j,0s,0n), 0s,2299161j)>,
# #<Date: 2020-09-14 ((2459107j,0s,0n), 0s,2299161j)>..#<Date: 2020-10-29 ((2459152j,0s,0n), 0s,2299161j)>,
# #<Date: 2020-03-21 ((2458930j,0s,0n), 0s,2299161j)>..#<Date: 2020-06-21 ((2459022j,0s,0n), 0s,2299161j)>,
# #<Date: 2020-02-07 ((2458887j,0s,0n), 0s,2299161j)>..#<Date: 2020-06-14 ((2459015j,0s,0n), 0s,2299161j)>,
# #<Date: 2020-07-02 ((2459033j,0s,0n), 0s,2299161j)>..#<Date: 2020-07-12 ((2459043j,0s,0n), 0s,2299161j)>]
接下來確定這些范圍涵蓋的最早和最晚日期。
earliest = arr.min_by { |r| r.begin }.begin
#=> #<Date: 2020-02-01 ((2458881j,0s,0n), 0s,2299161j)>
latest = arr.max_by { |r| r.end }.end
#=> #<Date: 2020-10-29 ((2459152j,0s,0n), 0s,2299161j)>
現在創建一個包含元素的陣列latest - earliest 1,所有元素都初始化為false:
covered = Array.new(latest - earliest 1, false)
#=> [false, false,..., false]
covered.size
#=> 272
如果任何一個范圍涵蓋一天,則每個范圍arr[i]都被考慮covered[d]后將相等。truearr[j], j <= iearliest d
我們現在可以計算期望的結果。
def calc1(earliest, covered, rng)
rng.count do |d|
offset = d-earliest
if covered[offset]
false
else
covered[offset] = true
end
end
end
arr.each_with_object({}).with_index do |(r,h),i|
h[ranges[i]] = calc1(earliest, covered, r)
end
#=> {"01.02.2020".."04.05.2020"=>94,
# "18.07.2020".."12.08.2020"=>26,
# "14.09.2020".."29.10.2020"=>46,
# "21.03.2020".."21.06.2020"=>48,
# "07.02.2020".."14.06.2020"=> 0,
# "02.07.2020".."12.07.2020"=>11}
請注意,前三個范圍不重疊。第四個范圍,"21.03.2020".."21.06.2020"有效地將第一個范圍擴大到"01.02.2020".."21.06.2020". 作為第五個范圍,"07.02.2020".."14.06.2020"完全在我們"07.02.2020".."14.06.2020"=> 0在回傳的哈希中獲得的修改后的第一個范圍內。
earliest我們可能會看到latest介于arr.
covered.filter_map.with_index do |d,i|
if d == false
dt = earliest i
"%s %s" % [Date::ABBR_MONTHNAMES[dt.mon], dt.day]
else
false
end
end
#=> ["Jun 22", "Jun 23", "Jun 24", "Jun 25", "Jun 26", "Jun 27", "Jun 28",
# "Jun 29", "Jun 30",
# "Jul 1",
# "Jul 13", "Jul 14", "Jul 15", "Jul 16", "Jul 17",
# "Aug 13", "Aug 14", "Aug 15", "Aug 16", "Aug 17", "Aug 18", "Aug 19",
# "Aug 20", "Aug 21", "Aug 22", "Aug 23", "Aug 24", "Aug 25", "Aug 26",
# "Aug 27", "Aug 28", "Aug 29", "Aug 30", "Aug 31",
# "Sep 1", "Sep 2", "Sep 3", "Sep 4", "Sep 5", "Sep 6", "Sep 7", "Sep 8",
# "Sep 9", "Sep 10", "Sep 11", "Sep 12", "Sep 13"]
這是
Jun 22-30
Jul 1
Jul 13-17
Aug 20-31
Sep 1-13
請參閱Enumerable#filter_map。
需要更復雜邏輯的更有效方法是covered包含“覆寫”天的非重疊范圍,按每個范圍的開始日期排序。當處理每個范圍r時arr:
r在不與任何范圍相交時添加到covered(在保持順序的位置);或者rcovered- 將一個范圍
covered修改為更大,并covered洗掉零個或多個相鄰范圍;或者 coveredr當被 中的一個范圍覆寫時不變covered。
這兩種方法等同于@Stefan 在對該問題的評論中建議的方法。
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/514659.html
標籤:数组红宝石算法日期日期差
下一篇:限制特定時間間隔的日期輸入
