Home > Blockchain >  How to group into 2 week increments by created_at [ruby]
How to group into 2 week increments by created_at [ruby]

Time:10-21

I have survey responses that I need to display in 2 week increments based on the created_at date. The output should be something like:

{10/1 : 4 10/15: 6 10/29: 3}

...where the first week is created from the earliest created_at date in the survey responses and the same for the last, but the latest created_at. I've seen things like group_by{ |s| s.created_at.month} but not something for every other week, starting on the Monday of the week. Any help would be much appreciated!

CodePudding user response:

You could calculate the number of days between the current record and the oldest and then use modulo 14:

oldest_date = YourModel.minimum(:created_at).to_date
your_relation.group_by { |record| 
  (record.created_at.to_date - oldest_date).modulo(14)
}

CodePudding user response:

You could define a method returning the year and the week range, for example:

def by_year_and_by_two_weeks(my_date)
  wk = my_date.strftime("%W").to_i/2
  wks = [wk, wk.odd? ? wk 1 : wk - 1].sort # <== adjust this
  [my_date.year, wks]
end

The %W in rails uses monday as the first day of the week.

So, when you have your object:

object.created_at #=> 2021-09-19 08:58:16.78053  0200
by_year_and_by_two_weeks(object.created_at) #=> [2021, [17, 18]]

Then you can use the method for grouping.

objects.group_by { |d| by_year_and_by_two_weeks(d.created_at) }

This is an example of result after values transformation to make it readables:

{[2021, [20, 21]]=>[2021-10-14 09:00:17.421142  0200, 2021-10-15 09:00:17.421224  0200, 2021-10-06 09:00:17.421276  0200, 2021-10-10 09:00:17.421328  0200], [2021, [18, 19]]=>[2021-09-22 09:00:17.421385  0200]}

Of course you can change the by_year_and_by_two_weeks return value as it best fits for you.

CodePudding user response:

Your requirements:

  • You want to group on the monday of the starting biweekly period.
  • You want the Hash key of the group to be the date of that monday.

I will also add another future-proofing requirement:

  • The start and end dates can be anywhere, even accross year boundaries.

If we take these requirements, and then utilize the modulo idea from spickermann, we can build it like this:

start_date = first_item.created_at.to_date.prev_occurring(:monday)

your_items.group_by { |item|
  item_date       = item.created_at.to_date
  days_from_start = item_date - start_date
  biweekly_offset = days_from_start.modulo(14)
  biweekly_monday = item_date - biweekly_offset
  biweekly_monday
}

Example:

test_dates = [
  Date.new(2021, 10, 1),
  Date.new(2021, 10, 6),
  Date.new(2021, 10, 10),
  Date.new(2021, 10, 13),
  Date.new(2021, 10, 20),
  Date.new(2021, 10, 31)
]

start = test_dates.first.prev_occurring(:monday)

pp test_dates.group_by { |date|
  days_from_start = date - start
  biweekly_offset = days_from_start.modulo(14)
  biweekly_monday = date - biweekly_offset
  biweekly_monday
}

Output:

{ Mon, 27 Sep 2021 => [Fri, 01 Oct 2021, Wed, 06 Oct 2021, Sun, 10 Oct 2021],
  Mon, 11 Oct 2021 => [Wed, 13 Oct 2021, Wed, 20 Oct 2021],
  Mon, 25 Oct 2021 => [Sun, 31 Oct 2021] }
  • Related