I have an array of DateTime objects like:
a = [
[Tue, 05 Mar 2013],
[Tue, 12 Mar 2013],
[Tue, 19 Mar 2013],
[Tue, 26 Mar 2013],
[Tue, 02 Apr 2013],
[Tue, 09 Apr 2013]
]
where a[0] is a Date object. I need to search for a specific date, like:
a.index('Tue, 06 Mar 2013'.to_date)
to find its index and delete everything before (and in another case after) this item. I need to search by any date, like in the example above, I'm searching by Tue, 05 Mar 2013, so it should be rounded to the nearest value: Tue, 05 Mar 2013. How could it be done in a Ruby way?
Instead of using dates, should be easier to use timestamps:
'Tue, 06 Mar 2013'.to_time.to_i
=> 1362528000
Higher the value, more in the future this date is.
If you are not inserting items in your list frequently, every time you insert a new item, sort it. When you find the index for the date , remove all other items. For example:
# Your dates list converted to timestamps
> times
=> [1362441600, 1363046400, 1363651200, 1364256000, 1364860800, 1365465600]
# Find the timestamp equal or greater than the given date
> correct_index_val = times.find{|x| x <= 'Tue, 27 Mar 2013'.to_time.to_i}
=> 1362441600 # this is actually the position for 26 Mar
# Position of the value equal or greater than the given date
> times.index(correct_index_val)
=> 3
# cutting the array in that point
> times[idx..-1]
=> [1364256000, 1364860800, 1365465600]
Here is the way :
I need to search it for specific date.
require 'date'
a = [
['Tue, 05 Mar 2013'],
['Tue, 12 Mar 2013'],
['Tue, 19 Mar 2013'],
['Tue, 26 Mar 2013'],
['Tue, 02 Apr 2013'],
['Tue, 09 Apr 2013']
]
nwar = a.flatten.map{|d| Date.parse(d)}
# point free style is - a.flatten.map(&Date.method(:parse))
srchdt = Date.parse('Tue, 06 Mar 2013')
p nwar.index(srchdt) # => nil
srchdt = Date.parse('Tue, 26 Mar 2013')
p nwar.index(srchdt) # => 3
After we have index of the item, I need to delete everything before (another case is to delete after) this item.
ind = nwar.index(srchdt) # => 3
nwar.shift(ind)
p nwar.map(&:to_s) # => ["2013-03-26", "2013-04-02", "2013-04-09"]
Related
I have a model with the created_at column and a state column (which may have different values, like A, nil, etc.), I want to get the amount of records I have in that model, first categorized by created_at and then by the state column, something like the following...
{
[Wed, 01 Jan 2020]=> { "A" => 662, nil => 1091 },
[Thu, 02 Jan 2020]=>{ "A" => 2895, nil => 5058 },
...
The closest I've gotten was this:
{
[Wed, 01 Jan 2020, "A"]=>662,
[Wed, 01 Jan 2020, nil]=>1091,
[Thu, 02 Jan 2020, "A"]=>2895,
[Thu, 02 Jan 2020, nil]=>5058,
...
With this query: MyModel.order('DATE(created_at) ASC').group('DATE(created_at)', 'state').count
How can I get what I want in one query? I'd like it to be as efficient as possible.
I have the next bidimensional array, where the first componente belongs to ActiveSupport::TimeWithZone and the second component is a string
[[Sun, 16 Jul 2017 14:41:56 -03 -03:00, "open"],
[Sun, 16 Jul 2017 14:41:56 -03 -03:00, "closed"],
[Sun, 16 Jul 2017 14:41:56 -03 -03:00, "closed"],
[Mon, 10 Jul 2017 00:00:00 -03 -03:00, "open"],
[Sun, 16 Jul 2017 14:45:31 -03 -03:00, "closed"],
[Sun, 16 Jul 2017 14:44:41 -03 -03:00, "open"],
[Sun, 16 Jul 2017 14:44:39 -03 -03:00, "closed"],
[Sun, 16 Jul 2017 14:44:13 -03 -03:00, "open"],
[Mon, 10 Jul 2017 00:00:00 -03 -03:00, "closed"],
[Fri, 14 Jul 2017 00:00:00 -03 -03:00, "open"],
[Mon, 17 Jul 2017 00:00:00 -03 -03:00, "open"]]
I need to convert that array in efficient way into
{["09-Jul", "open"]=>2, ["16-Jul", "open"]=>1, ["09-Jul", "closed"]=>0, ["16-Jul", "closed"]=>1}
That is, I need to convert the first component into the format %b-%d. Also, I need group by week and "status". Finally I need to count these grouped values and present the data with hash format as the second example
input.group_by { |d, v| [d.strftime('%b-%d'), v] }
.map { |k, v| [k, v.count] }.to_h
Also, for Ruby 2.4+ it could be simplified (credits go to #MarkThomas) to:
input.group_by { |d, v| [d.strftime('%b-%d'), v] }
.transform_values(&:count)
You could create the desired hash with the form of Hash::new that takes an argument equal to the default value of the hash, which here we want to be zero. What that means is that if a hash h, defined h = Hash.new(0), does not have a key k, then h[k] returns the default value (here 0), without modifying the hash h.
input.each_with_object(Hash.new(0)) { |(d,v),h| h[[d.strftime('%b-%d'), v]] += 1
I'm looking to check for consecutive dates and display them differently if they are consecutive.
I'm working with Garage Sales that have multiple dates per sale. I'd like to then cycle through each date, and group any consecutive dates to display as a group: Ex: Apr 28 - Apr 30
I also need to account for non-consecutive dates:
Ex: Apr 15, Apr 28 - 30
Additionally, weekly dates need to be recognized as non-consecutive (so basically avoid step checks):
Ex: Apr 16, Apr 23, Apr 30
So far, I'm taking each date that hasn't passed & ordering them properly.
garage_sale_dates.where("date > ?",Time.now).order(:date)
Thanks for any help! Let me know if any other info is needed.
You can use the slice_before method of Enumerable
dates = [Date.yesterday, Date.today, Date.tomorrow, Date.parse('2016-05-01'), Date.parse('2016-05-02'), Date.parse('2016-05-05')]
# => [Wed, 27 Apr 2016, Thu, 28 Apr 2016, Fri, 29 Apr 2016, Sun, 01 May 2016, Mon, 02 May 2016, Thu, 05 May 2016]
prev = dates.first
dates.slice_before { |d| prev, prev2 = d, prev; prev2 + 1.day != d }.to_a
# => [[Wed, 27 Apr 2016, Thu, 28 Apr 2016, Fri, 29 Apr 2016], [Sun, 01 May 2016, Mon, 02 May 2016], [Thu, 05 May 2016]]
Then you can simply join the 2-or-more-element arrays from the result with "-", and leave the single element arrays intact:
prev = dates.first
dates.slice_before { |d| prev, prev2 = d, prev; prev2 + 1.day != d }.
map{|d| d.size > 1 ? "#{d.first.to_s} - #{d.last.to_s}" : d.first.to_s }
# => ["2016-04-27 - 2016-04-29", "2016-05-01 - 2016-05-02", "2016-05-05"]
There is even a commented example in the docs that is technically equivalent to yours (but deals with integers, not dates).
a simple script would do the trick
def date_list(dates)
result = []
dates.each do |date|
if result.empty? || (date - result.last.last).to_i != 1
result << [date]
else
result.last << date
end
end
result
end
# make sure dates is an array of dates
dates = [Thu, 28 Apr 2016, Fri, 29 Apr 2016, Sun, 01 May 2016, Mon, 02 May 2016, Tue, 03 May 2016, Thu, 05 May 2016, Fri, 06 May 2016, Sat, 07 May 2016, Sun, 08 May 2016]
#this would give you an array of date ranges that you wanted
date_list(dates)
=> [
[Thu, 28 Apr 2016, Fri, 29 Apr 2016],
[Sun, 01 May 2016, Mon, 02 May 2016, Tue, 03 May 2016],
[Thu, 05 May 2016, Fri, 06 May 2016, Sat, 07 May 2016, Sun, 08 May 2016]
]
I found a simpler solution using chunk_while method (available from Ruby 2.4.6).
Based on BoraMa answer:
dates = [Date.yesterday, Date.today, Date.tomorrow, Date.parse('2020-05-01'), Date.parse('2020-05-02'), Date.parse('2020-05-05')]
# => [Thu, 26 Mar 2020, Fri, 27 Mar 2020, Sat, 28 Mar 2020, Fri, 01 May 2020, Sat, 02 May 2020, Tue, 05 May 2020]
dates.chunk_while { |date_before, date_after| (date_after - date_before).to_i == 1 }.to_a
# => [[Thu, 26 Mar 2020, Fri, 27 Mar 2020, Sat, 28 Mar 2020], [Fri, 01 May 2020, Sat, 02 May 2020], [Tue, 05 May 2020]]
I think is more readable and it needs less steps to get same result.
Using a Date you can do the subtraction, it would be something like:
date1 = 1.day.ago
date2 = 2.day.ago
(date1.to_date - date2.to_date).to_i
=> 1
Adding onto Matouš Borák's answer, if you want to change the date format such that it would match what was asked in the question i.e. Apr 15, Apr 28 - 30
dates = [Date.yesterday, Date.today, Date.tomorrow, Date.parse('2021-10-01'), Date.parse('2021-10-02'), Date.parse('2021-10-05')]
# => [Wed, 01 Sep 2021, Thu, 02 Sep 2021, Fri, 03 Sep 2021, Fri, 01 Oct 2021, Sat, 02 Oct 2021, Tue, 05 Oct 2021]
prev = dates.first
dates.slice_before { |d| prev, prev2 = d, prev; prev2 + 1.day != d }.
map{|d| d.size > 1 ? "#{d.first.strftime('%b %d')} - #{d.last.strftime('%d')}" : d.first.strftime('%b %d') }
# => ["Sep 01 - 03", "Oct 01 - 02", "Oct 05"]
I have a problem with calculation on datetime fields.
I have two variables:
a = Sat, 01 Feb 2014 13:00:00 CET +01:00
and
b = Fri, 28 Feb 2014 13:00:00 CET +01:00
And I want to calculate how many days passed from a to b range.
Please Help.
You can use this.
require 'date'
( Date.parse('Fri, 28 Feb 2014 13:00:00 CET +01:00') - Date.parse('Sat, 01 Feb 2014 13:00:00 CET +01:00')).to_i
=> 27
i.e
( Date.parse(b) - Date.parse(a)).to_i
require 'date'
a=Date.parse('Sat, 01 Feb 2014 13:00:00 CET +01:00')
=> #<Date: 2014-02-01 (4913379/2,0,2299161)>
b=Date.parse('Fri, 28 Feb 2014 13:00:00 CET +01:00')
=> #<Date: 2014-02-28 (4913433/2,0,2299161)>
b-a
=> (27/1)
(b-a).to_i
=> 27
You can also use time_diff gem to calculate the difference.
In your gem file add gem 'time_diff'
I have added the code in index.html.erb but, you can use it any where you want.
take a look on following code.
<% require 'time_diff' %>
<% a = Time.diff(Time.parse('Sat, 01 Feb 2014 13:00:00 CET +01:00'), Time.parse('Fri, 28 Feb 2014 13:00:00 CET +01:00'), "%d") %>
<%= a[:diff] %>
and I got the following result
"27 days"
You can also take a look on
https://github.com/abhidsm/time_diff
I have an Rails 4 application that collects attendance at church services. Some weeks there are two services and some weeks there is only one. I need to get the total attendance for each week and show it as a graph.
By calling:
Stat.calculate(:sum, :attendance, group: :date)
in the console I have been able to collect the data in a hash like this:
{Sun, 06 Jan 2013=>66, Sun, 13 Jan 2013=>65, Sun, 20 Jan 2013=>60, Sun, 27 Jan 2013=>67, Sun, 03 Feb 2013=>60, Sun, 10 Feb 2013=>76, Sun, 17 Feb 2013=>65, Sun, 24 Feb 2013=>52, Sun, 03 Mar 2013=>52, Sun, 10 Mar 2013=>45, Sun, 17 Mar 2013=>56, Sun, 24 Mar 2013=>134, Sun, 31 Mar 2013=>76, Sun, 07 Apr 2013=>88, Sun, 14 Apr 2013=>87, Sun, 28 Apr 2013=>93, Sun, 05 May 2013=>93, Sun, 12 May 2013=>95, Sun, 19 May 2013=>90, Sun, 26 May 2013=>87, Sun, 02 Jun 2013=>71, Sun, 09 Jun 2013=>86, Sun, 16 Jun 2013=>109, Sun, 23 Jun 2013=>80, Sun, 30 Jun 2013=>68, Sun, 07 Jul 2013=>75, Sun, 14 Jul 2013=>73}
But What I need for my chart is an array of hashes in the form of:
{date: "Sun, 23 Jun 2013", attendance: 80}, {date: "Sun, 30 Jun 2013", attendance: 68"}
So I am trying to figure out how to convert the first form into the second form.
I'm sure its something pretty easy, but my limited rails knowledge is hitting a wall.
.collect{|key,value| {:date => key, :attendance => value} }
loop through and create a new hash where the original key becomes the value for date and the original value becomes the value for attendance. These new hashes are collected into an array.
You can think as below:
h = {"Sun, 06 Jan 2013"=>66, "Sun, 13 Jan 2013"=>65, "Sun, 20 Jan 2013"=>60 }
h.map{|k,v| Hash[:date,k,:attend,v]}
# => [{:date=>"Sun, 06 Jan 2013", :attend=>66},
# {:date=>"Sun, 13 Jan 2013", :attend=>65},
# {:date=>"Sun, 20 Jan 2013", :attend=>60}]