2017-08-19 147 views
1

空的幾個月裏,我有爲期一個月的哈希值>值獲取哈希

{"Apr 2016"=>6, "Aug 2016"=>9, "Jan 2017"=>11, "Apr 2017"=>6, "May 2017"=>9, "Jun 2017"=>1, "Jul 2017"=>9} 

我想知道什麼是獲得第一和最後一個值

我會之間的所有空個月的最佳途徑像類似

{"Apr 2016"=>6, May 2016=>0, Jun 2016=>0 .... "Aug 2016"=>9, "Sep 2016" => 0 "Jan 2017"=>11, "Apr 2017"=>6, "May 2017"=>9, "Jun 2017"=>1, "Jul 2017"=>9} 
+2

你見過「[在兩個日期之間獲取獨特的月份和年份數組](https://stackoverflow.com/questions/45064402/get-unique-array-of-month-and-years-between-two-日期/ 45064684)「?這個問題將第一個和最後一個月之間的所有月份都當作一個數組......並且如果能得到第一個和最後一個月,那麼這些答案可以很容易地修改以填充散列的缺失月份。 –

回答

4

這裏的另一種方式,用each_with_object

def add_months(dates) 
    min, max = dates.keys.map { |date| Date.parse(date) }.minmax 
    range = (min..max).map { |date| date.strftime("%b %Y") }.uniq 

    range.each_with_object({}) { |date, result| result[date] = dates[date] || 0 } 
end 

輸出:

dates = {"Apr 2016"=>6, "Aug 2016"=>9, "Jan 2017"=>11, "Apr 2017"=>6, "May 2017"=>9, "Jun 2017"=>1, "Jul 2017"=>9} 

add_months(dates) 
#=> { 
#  "Apr 2016"=>6, 
#  "May 2016"=>0, 
#  "Jun 2016"=>0, 
#  "Jul 2016"=>0, 
#  "Aug 2016"=>9, 
#  "Sep 2016"=>0, 
#  "Oct 2016"=>0, 
#  "Nov 2016"=>0, 
#  "Dec 2016"=>0, 
#  "Jan 2017"=>11, 
#  "Feb 2017"=>0, 
#  "Mar 2017"=>0, 
#  "Apr 2017"=>6, 
#  "May 2017"=>9, 
#  "Jun 2017"=>1, 
#  "Jul 2017"=>9 
# } 
+1

'.uniq'不好,但它是通過簡單表達式來完成的唯一方法,+1 – tokland

+0

@tokland,別忘了我們有[日期#>>](http:// ruby​​-doc。 org/stdlib-2.4.0/libdoc/date/rdoc/Date.html#method-i-3E-3E),它避免了在範圍的開始和結束之間的所有日期都需要生成一個Date對象。 –

+0

@CarySwoveland的確如此,但是試圖用它來表達習慣以獲得範圍(沒有時間等),我發現沒有簡單而習慣的方法。 – tokland

1

您可以用下面的方法做上述:

def normalize_months(month_values) 
    ordered_months = month_values.keys.map do |m| Date.parse(m) end.sort.map do |s| s.strftime('%b %Y') end 
    normalized = [] 
    current = ordered_months.first 
    while current != ordered_months.last do 
    normalized << current 
    current = Date.parse(current).next_month.strftime('%b %Y') 
    end 
    result = {} 
    normalized.each do |g| result[g] = month_values[g].nil? ? 0 : month_values[g] end 
    result 
end 

如果您使用純紅寶石進行此操作,請在此之上執行require 'date'

0

我建議像下面這樣的方法:

require 'date' 

def add_missing_months(dates) 
    # get all months 
    months = dates.keys.map{|m| Date.parse(m)} 

    # get min and max 
    min = months.min 
    max = months.max 

    # collect all missing months 
    missing_months = {} 
    while min < max 
     min = min.next_month 

     missing_months[min.strftime('%b %Y')] = 0 unless months.include?(min) 
    end 

    # merge hashes 
    dates.merge(missing_months) 
end 
3

代碼

require 'date' 

def fill_in_missing_months(dates) 
    date_fmt = '%b %Y' 
    fm, lm = dates.keys. 
       minmax_by { |date| Date.strptime(date, date_fmt) }. 
       map  { |date| Date.strptime(date, date_fmt) } 
    (0..12*(lm.year-fm.year) + lm.month-fm.month).each_with_object({}) do |_,h| 
    str = fm.strftime(date_fmt) 
    h[str] = dates.fetch(str, 0) 
    fm >>= 1 
    end 
end 

dates = {"Apr 2016"=>6, "Aug 2016"=>9, "Jan 2017"=>11, "Apr 2017"=>6, 
     "May 2017"=>9, "Jun 2017"=>1, "Jul 2017"=>9} 

fill_in_missing_months(dates) 
    #=> {"Apr 2016"=>6, "May 2016"=>0, "Jun 2016"=>0, "Jul 2016"=>0, "Aug 2016"=>9, 
    # "Sep 2016"=>0, "Oct 2016"=>0, "Nov 2016"=>0, "Dec 2016"=>0, "Jan 2017"=>11, 
    # "Feb 2017"=>0, "Mar 2017"=>0, "Apr 2017"=>6, "May 2017"=>9, "Jun 2017"=>1, 
    # "Jul 2017"=>9} 

說明

經驗Rubiests:一個血淋淋的細節,如下公告已經發布,所以你可以跳過我的答案的其餘部分。

Ruby在解析代碼時將fm >>= 1擴展爲fm = fm >> 1Date#>>提前日期由其參數給出的月數,這裏是1

除了:>>,看該文檔的方法Integer#timesHash#fetchDate::strptimeDate#strftimeDate#yearDate#monthEnumerator#with_objectEnumerable#minimax_by(不包括更常用的方法如Enumerable#map)。回憶#Date#year表示實例方法,而::[Date::strptime]表示類方法。

對於示例中給出的dates,其步驟如下。

date_fmt = '%b %Y' 
    b = dates.keys 
    #=> ["Apr 2016", "Aug 2016", "Jan 2017", "Apr 2017", "May 2017", 
    # "Jun 2017", "Jul 2017"] 
    c = b.minmax_by { |date| Date.strptime(date, date_fmt) } 
    #=> ["Apr 2016", "Jul 2017"] 
    fm, lm = c.map { |date| Date.strptime(date, date_fmt) } 
    #=> [#<Date: 2016-04-01 ((2457480j,0s,0n),+0s,2299161j)>, 
    # #<Date: 2017-07-01 ((2457936j,0s,0n),+0s,2299161j)>] 
    fm #=> #<Date: 2016-04-01 ((2457480j,0s,0n),+0s,2299161j)> 
    lm #=> #<Date: 2017-07-01 ((2457936j,0s,0n),+0s,2299161j)>] 
    d = 0..12*(lm.year-fm.year) + lm.month-fm.month 
    #=> 0..15 
    e = d.each_with_object({}) 
    #=> #<Enumerator: 0..15:each_with_object({})> 

我們可以通過將其轉換爲一個數組,使用Enumerable#entries(或Enumerable#to_a)看到將由e生成並傳遞到該塊中的值。

e.entries 
    #=> [[0, {}], [1, {}],..., [15, {}]] 

在這些元組的哈希最初是空的,但隨着塊執行計算將被填充。

第一元件由e,它被傳遞給塊產生和塊變量被設置等於它的值,使用一種稱爲消歧分解過程刺出與每個塊變量相關聯的部分。

_,h = e.next 
    #=> [0, {}] 
h #=> {} 

我使用_作爲第一塊變量以表示它(指數)沒有在塊計算中使用。繼續,

str = fm.strftime(date_fmt) 
    #=> "Apr 2016" 
h[str] = dates.fetch(str, 0) 
    #=> 6 
h #=> {"Apr 2016"=>6} 

在這種情況下dates有一個關鍵"Apr 2016",所以h["Apr 2016"]設置等於dates["Apr 2016"]。在其他情況下,dates將不具有等於str(例如"May 2016")的密鑰,因此該值將被設置爲等於fetch的默認值0

fm >>= 1 
    #=> #<Date: 2016-05-01 ((2457510j,0s,0n),+0s,2299161j)> 

fm現在是2016年5月。其餘的計算是類似的。

+0

像往常一樣優秀的答案。 – Gerry

+0

謝謝,@簡單。修復它(當穿着一種羞怯的表情時)。 –