2

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.

0

4 Answers 4

2

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
Sign up to request clarification or add additional context in comments.

Comments

0

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

5 Comments

Part of the complexity of the question is how do you get the end date? Thanks though if I'm able to create the method on my own using the info for the answer I'll give you credit :]
do you want the challenge with the most current date?
I'm trying to create an array of dates using a method dates_challenged. I give an example of what that would output if date_started was today, days_challenged was 5, and committed was weekdays. Hope that is clearer. Sorry not too clear on your question.
perhaps you can revise your question to convey intent a little clearer and simpler? it is hard for me to tell what you need here
Updated question. Sorry
0

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"]

Comments

0
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"]

3 Comments

Yea I'm trying to create a method dates_challenged in rails. Thanks for trying to help me out again. Problem with the output though. That shows 5 days in a row. The example I gave uses committed days, which didn't include weekends.
Updated my answer.
Thanks U! I'm trying to make a method though so I could do @challenge.dates_challenged where then I can iterate over the array of dates in a view. Sorry for the confusion.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.