问题
If a user creates a challenge with days_challenge: 5
& committed: ["mon", "tue", "wed", "thu", "fri"]
then how can we create an array of dates from date_started: "2016-04-20"
until the last day of the challenge using a model method called dates_challenged
?
create_table "challenges", force: true do |t|
t.string "action"
t.date "date_started"
t.text "committed", default: "---\n- sun\n- mon\n- tue\n- wed\n- thu\n- fri\n- sat\n"
t.integer "days_challenged"
end
The array would look something like this: ["2016-04-20", "2016-04-21", "2016-04-22", "2016-04-25", "2016-04-26"]
class Challenge < ActiveRecord::Base
serialize :committed, Array
def dates_challenged
# Not sure if a model method is enough or if I'd have to migrate a new column
end
def committed_wdays
committed.map do |day|
Date::ABBR_DAYNAMES.index(day.titleize)
end
end
def days_left_challenged
def days_done_challenged
((date_started.to_date)..Date.yesterday).count do |date|
committed_wdays.include? date.wday
end
end
if self.days_done_challenged >= self.days_challenged
0
else
self.days_challenged - ((date_started.to_date)..Date.yesterday).count do |date|
committed_wdays.include? date.wday
end
end
end
end
To clarify, I'm trying to make a model method so I could do @challenge.dates_challenged
in a view where I can then iterate over the array of dates.
回答1:
This is what I came up with - there are still some issues - I think it will blow up if committed
is empty, but it's nice and concise:
def dates_challenged
date = date_started-1
days_challenge.times.map { date = find_next(date) }
end
private
def find_next(date)
break if committed.include? Date::ABBR_DAYNAMES[date.wday].downcase while date = date.next
date
end
回答2:
Get the start date and end date - then you can get a range of dates and or days between the start and the end
start_date = challenge.date.to_date
end_date = another_challenge.date.to_date
(start_date..end_date).map(&:mday) # returns array of days of the month
see List Array of Days Between Two Dates for more information
回答3:
Simple and verbose: create a date object from your date string (Date.parse), and create an empty array to hold the dates that you are 'committing' to. The list of days of the week that are available is stored in an array, as above. Loop until the number of days we are committing to stored in the array is equal to how many days were requested, adding one day to each previous day. Dates added to the date_committed array are checked against the valid days of week array (days_committed).
require 'date'
days_challenge = 5
days_committed = ["mon", "tue", "wed", "thu", "fri"]
date_started = "2016-04-20"
date_committed = []
date_started_parsed = Date.parse(date_started)
while (date_committed.size < days_challenge)
date_dow = date_started_parsed.strftime('%a').downcase
if (days_committed.include?(date_dow))
date_committed << date_started_parsed.strftime('%F')
end # if
date_started_parsed = date_started_parsed + 1
end # while
puts date_committed.to_s
Returns the correct dates based on valid days of the week.
["2016-04-20", "2016-04-21", "2016-04-22", "2016-04-25", "2016-04-26"]
回答4:
committed = ["mon", "tue", "wed", "thu", "fri"]
days = committed.dup
count = 0
dates = []
while days.present?
count += 1
weekday = (date + count).strftime('%a').downcase
if days.include? weekday
dates << (date + count.days).strftime('%Y-%m-%d')
days.delete(weekday)
end
end
puts dates # => ["2016-04-21", "2016-04-22", "2016-04-25", "2016-04-26", "2016-04-27"]
Previuos answer
Assuming you are in Rails environment:
days_challenge = 5
date = DateTime.parse("2016-04-20")
(1..days_challenge).map {|n| (date + n.days).strftime('%Y-%m-%d') }
# Outputs
["2016-04-21", "2016-04-22", "2016-04-23", "2016-04-24", "2016-04-25"]
来源:https://stackoverflow.com/questions/36755716/how-to-make-integer-into-array-of-dates