Pull in current month/year and previous month/year in Ruby [closed] - ruby

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Closed 8 years ago.
This question appears to be off-topic because it lacks sufficient information to diagnose the problem. Describe your problem in more detail or include a minimal example in the question itself.
Questions asking for code must demonstrate a minimal understanding of the problem being solved. Include attempted solutions, why they didn't work, and the expected results. See also: Stack Overflow question checklist
Improve this question
I am creating a 12 month line chart that has data for the previous 12 months. The current month, or 12th line item, should say OCT 13. The first line item should say OCT 12.
How can I write something to dynamically pull in the current month as well as the previous 11 months, all the way back to the current month in last year. The issue I am having is making sure that last October gets tagged as a month in 2012, but in 3 months I need January to be tagged as 2013 without me changing the code.

a = [Date.today.prev_year]
12.times{a.push(a.last.next_month)}
a.map{|d| d.strftime("%^b %y")}
# => [
"OCT 12",
"NOV 12",
"DEC 12",
"JAN 13",
"FEB 13",
"MAR 13",
"APR 13",
"MAY 13",
"JUN 13",
"JUL 13",
"AUG 13",
"SEP 13",
"OCT 13"
]

Use << operator to shift date by a month
require "date"
12.downto(0).map{ |d| (Date.today << d).strftime("%^b %y") }
#=> ["OCT 12", "NOV 12", "DEC 12", "JAN 13", "FEB 13", "MAR 13", "APR 13",
# "MAY 13", "JUN 13", "JUL 13", "AUG 13", "SEP 13", "OCT 13"]
Used #Stefan's input to change the order.

require 'date'
def last_n_months(n, format='%^b %Y')
(n+1).times.map { |i| (Date.today << i).strftime(format) }
end
last_n_months(12)
# => ["OCT 2013", "SEP 2013", "AUG 2013", "JUL 2013", "JUN 2013", "MAY 2013", "APR 2013", "MAR 2013", "FEB 2013", "JAN 2013", "DEC 2012", "NOV 2012", "OCT 2012"]

Yes, use of the Date#methods is the way to go here, and yes, they are needed to get today's date, but wouldn't it be more satisfying to roll-your-own? Here's one way:
# Assume start_year falls in current millenium. 1 <= start_month <= 12
MONTHS = %q[JAN FEB MAR APR MAY JUN JUL AUG SEP OCT NOV DEC].split
def doit(start_year, start_month)
pairs = (((start_month-1)..11).to_a.product [start_year-1]) +
((0..(start_month-1)).to_a.product [start_year])
pairs.map! {|p| MONTHS[p.first] + " #{p.last.to_s}"}.reverse
end
p doit(13, 10)
First create pairs =>
[[9, 12], [10, 12], [11, 12]] +
[[0, 13], [1, 13], [2, 13], [3, 13], [4, 13], [5, 13], [6, 13], [7, 13], [8, 13], [9, 13]]
which is
[[9, 12], [10, 12], [11, 12], [0, 13], [1, 13], [2, 13], [3, 13],
[4, 13], [5, 13], [6, 13], [7, 13], [8, 13], [9, 13]]
Then replace elements of pairs with a string containing the month abbreviation and year.
["OCT 13", "SEP 13", "AUG 13", "JUL 13", "JUN 13", "MAY 13", "APR 13",
"MAR 13", "FEB 13", "JAN 13", "DEC 12", "NOV 12", "OCT 12"]
I evidently need all the parentheses in calculating pairs. Can anyone explain why I need the outer ones on the second line?

Related

How to iterate an array every 30 items

I have an array of products with 234 items.
I need to create another array with a pagination (every 10 items)
example:
[
[1,2,3,4,5,6,7,8,9,10],
[1,2,3,4,5,6,7,8,9,10],
[1,2,3,4,5,6,7,8,9,10],
...
]
How can I solve this?
I've tried in_groups_of but I don't have success.
You're looking for each_slice
Whenever you have an array problem, check the Enumerable. in_groups_of is a Rails method and uses each_slice under the hood.
Just use Enumerable#each_slice
[*1..34].each_slice(10).to_a
# =>
# [
# [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
# [11, 12, 13, 14, 15, 16, 17, 18, 19, 20],
# [21, 22, 23, 24, 25, 26, 27, 28, 29, 30],
# [31, 32, 33, 34]
# ]

How to find the latest Date for every number in draw, from Map<Date, List<Integer>>

Let say I have a Map<Date, List<Integer>>, where list of integers is just a list of numbers thrown in lottery draw. It may look like this:
Wed Nov 15 13:31:45 EST 2017=[1, 2, 3, 4, 5, 6],
Wed Nov 22 13:31:45 EST 2017=[7, 8, 9, 10, 11, 12],
Wed Nov 29 13:31:45 EST 2017=[13, 14, 15, 16, 17, 18],
Wed Dec 13 13:31:45 EST 2017=[1, 19, 20, 21, 22, 23],
Wed Dec 20 13:31:45 EST 2017=[24, 25, 26, 27, 28, 29]
I need to convert that map into the map, where key is the lottery number, and the value is the last date when the number was thrown. Something like:
1=Wed Dec 13 13:31:45 EST 2017
2=Wed Nov 15 13:31:45 EST 2017
etc till 49.
So, the question is: is it possible to make it with the Java 8 streams and if yes, then how.
Thanks in advance.
If I am not mistaken you are looking for something like this (assuming Date is Comparable):
map.entrySet()
.stream()
.flatMap(x -> x.getValue().stream().map(y -> new AbstractMap.SimpleEntry<>(x.getKey(), y)))
.collect(Collectors.groupingBy(
Entry::getValue,
Collectors.collectingAndThen(
Collectors.maxBy(Comparator.comparing(Entry::getKey)),
x -> x.get().getKey())));
Here's a succint way to do it (without streams, though):
Map<Integer, Date> result = new HashMap<>();
map.forEach((date, list) -> list.forEach(n -> result.merge(n, date,
(oldDate, newDate) -> newDate.after(oldDate) ? newDate : oldDate)));
This iterates the map map and for each one of its (date, list) pairs, it iterates the list list of numbers. Then it uses the Map.merge and Date.after methods to put entries into the result map in such a way that only the last date is mapped to a given number.

using predefined array as an argument in a case block

im new to ruby but i would like to create a case block which uses arrays (or something similar as the argument)
here is what i have in mind
thirty_one_days_month = [1, 3, 5, 7, 8, 10, 12]
thirty_days_month = [4, 6, 9, 11]
case month
when thirty_one_days_month #instead of 1, 3, 5, 7, 8, 10, 12
#code
when thirty_days_month #instead 4, 6, 9, 11
#code
i know this code wont work but is this at all possible?
Use the splat operator:
case month
when *thirty_one_days_month
#code
when *thirty_days_month
#code
end
Anyway, that's how I'd write it:
days_by_month = {1 => 31, 2 => 28, ...}
case days_by_month[month]
when 31
# code
when 30
# code
end
You can use a case statement like this:
case
when thirty_one_days_month.include?(month)
puts "31 day month"
when thirty_days_month.include?(month)
puts "30 day month"
else
puts "February"
end

create a range of months between two dates in ruby

I need to create logfiles per month for a range of months.
Therefor I need all [year,month] tuples in a given range
How do you do iterating over dates?
How can this be done if I'd need to iterate every day?
For example:
((Date.today - 90)..Date.today).map{|d| [d.year, d.month]}.uniq
#=> [[2012, 12], [2013, 1], [2013, 2], [2013, 3]]
Ruby Date supports producing successive days and offers a next_month method which could be used to efficiently iterate over months.
Here's a generic method that adapts to the precision of your inputs:
require 'date'
def date_tuples(from,to)
prec = from.size
start = Date.new(*from)
finish = Date.new(*to)
filter_on = [:day,:mon].first(3-prec)
filter = ->(d) { filter_on.all? {|attr| d.send(attr) == 1 } }
(start..finish)
.select(&filter)
.map { |d| [d.year,d.mon,d.day].first(prec) }
end
[7] pry(main)> date_tuples([2012],[2015])
=> [[2012], [2013], [2014], [2015]]
[8] pry(main)> date_tuples([2012,10],[2013,3])
=> [[2012, 10], [2012, 11], [2012, 12], [2013, 1], [2013, 2], [2013, 3]]
[9] pry(main)> date_tuples([2012,10,25],[2012,11,6])
=> [[2012, 10, 25],
[2012, 10, 26],
[2012, 10, 27],
[2012, 10, 28],
[2012, 10, 29],
[2012, 10, 30],
[2012, 10, 31],
[2012, 11, 1],
[2012, 11, 2],
[2012, 11, 3],
[2012, 11, 4],
[2012, 11, 5],
[2012, 11, 6]]
I came up with this solution to generate a list of all [year,month] tuples in the range:
first=[2012,10]
last=[2013,03]
(first[0]..last[0]).to_a.product((1..12).to_a).select{|ym|(first..last).cover?(ym)}
=> [[2012, 10], [2012, 11], [2012, 12], [2013, 1], [2013, 2], [2013, 3]]
start_date = 1.year.ago.to_date
end_date = Date.current.yesterday
monthly = [Date.new(start_date.year, start_date.beginning_of_month.month, 1)]
(start_date..end_date).each do |d|
month_date = Date.new(d.year, d.next_month.beginning_of_month.month, 1)
monthly << month_date if monthly.exclude?(month_date) && month_date < end_date - 1.month
end
monthly
=> [Fri, 01 Sep 2017, Sun, 01 Oct 2017, Wed, 01 Nov 2017, Fri, 01 Dec 2017, Sun, 01 Jan 2017, Thu, 01 Feb 2018, Thu, 01 Mar 2018, Sun, 01 Apr 2018, Tue, 01 May 2018, Fri, 01 Jun 2018, Sun, 01 Jul 2018, Wed, 01 Aug 2018]
require 'date'
Time.new(2011).to_date.upto(Time.now.to_date) do |a|
puts ""+a.day.to_s+","+a.month.to_s+","+a.year.to_s
end
Or getting your month/year tuples:
require 'date'
result = []
Time.new(2002).to_date.upto(Time.now.to_date) do |a|
result << [a.month,a.year]
end
result.uniq!
Use the upto method from date: http://ruby-doc.org/stdlib-2.0/libdoc/date/rdoc/Date.html#method-i-upto
Here is a way i wrote to solve this issue.
This was designed for working with hash data like:
{Sun, 01 Jan 2012=>58, Wed, 01 Feb 2012=>0, Thu, 01 Mar 2012=>0}
but could be easily modified for array data.
See: https://github.com/StephenOTT/add_missing_dates_ruby where i have provided a working code sample
But the key piece of code is:
def addMissingMonths (datesHash)
count = 0
result = {}
datesHash.keys.each do |x|
if x != datesHash.keys.last
(x+1.month).upto(datesHash.keys[count+1]-1.month) do |a|
result[a.at_beginning_of_month] = 0
end
end
count += 1
end
return result.merge!(datesHash)
end
The key content to look at is: (x+1.month).upto(datesHash.keys[count+1]-1.month)
Most of the answers here require iterating over every day in the range. Meaning if you are doing this for a range of a few years, you could be having a loop with thousands of iterations.
This snippet creates a loop with as many steps as there are months in the range, which is more efficient:
require 'date'
start_date = Date.new(2010, 10)
end_date = Date.new(2011, 4)
current_month = start_date
date_tuples = []
while current_month <= end_date
date_tuples << [current_month.year, current_month.month]
current_month = current_month.next_month
end
pp date_tuples
# => [[2010, 10], [2010, 11], [2010, 12], [2011, 1], [2011, 2], [2011, 3], [2011, 4]]
One quirk of this method is that it will only work with dates that are on the first day of the month. So if you have a date like Date.new(2020, 10, 12) you need to convert it to the first day of the month
I had a slighly different problem, but it is related: I needed the months between a given start & end year.
def month_start_dates_between_years(start_year, end_year)
years = (start_year..end_year).to_a
month_tuples = years.product((1..12).to_a)
return month_tuples.map { |tuple| Date.new(*tuple) }
end

Motorsport Lap Chart Revisited

This is my first time posting so I apologize if asking a new question based on a pre-exisitng one is bad form.
I am trying to create a "lap chart" similar to the one presented here:
Motorsport Lap Chart using ListLinePlot
My need has a bit of a twist. I am charting the lap positions for a different sport and my data is structured differently. The data is listed by team and the position the team was in at the completion of each lap. For example, here is a sample of the data:
BRA = {03, 01, 01, 01};
CAN = {14, 14, 06, 10};
CRO = {10, 08, 11, 12};
DEN = {05, 05, 04, 11};
FRA = {13, 11, 14, 13};
GBR = {04, 02, 02, 02};
GER = {15, 15, 12, 09};
GRE = {16, 16, 16, 16};
IRL = {01, 03, 08, 06};
NOR = {02, 04, 09, 05};
NZL = {06, 06, 05, 08};
POL = {08, 07, 03, 03};
POR = {11, 13, 15, 15};
SUI = {07, 09, 10, 07};
SWE = {09, 10, 07, 04};
USA = {12, 12, 13, 14};
In this example Brazil was in third place at the end of the first lap and then moved into first by the end of the second, holding the place for the third lap and then to the finish.
I have created the chart I need by manually retyping the data in a format to fit the code posted in the earlier example, but there has to be a more efficient way. Any suggestions are appreciated.
One other question... currently the positions at the end of a given lap are spaced evenly apart, however in the real race first place was 20 seconds ahead of second who was perhaps 10 seconds ahead of third. Is there a way to plot each position to reflect the relative time ahead of the next competitor?
Here is my current working code:
data = {{09, 10, 01, 06, 04, 11, 14, 12, 15, 03, 13, 16, 05, 02, 07, 08},
{01, 06, 09, 10, 04, 11, 12, 03, 14, 15, 05, 16, 13, 02, 07, 08},
{01, 06, 12, 04, 11, 02, 15, 09, 10, 14, 03, 07, 16, 05, 13, 08},
{01, 06, 12, 15, 10, 09, 14, 11, 07, 02, 04, 03, 05, 16, 13, 08}};
{p, n} = {Max#data, Length#data};
ListLinePlot[
Replace[Array[data~Position~# &,
p], {lap_, y_} :> {lap - 1, -y}, {2}],
Frame -> True,
FrameLabel -> {"Rounding", "Position", "Rounding",
"Final Positions"}, GridLines -> {Range[0, n + 1], None},
FrameTicks -> {
{{{-1, "IRL"}, {-2, "NOR"}, {-3, "BRA"}, {-4, "GBR"}, {-5,
"DEN"}, {-6, "NZL"}, {-7, "SUI"}, {-8, "POL"}, {-9,
"SWE"}, {-10, "CRO"}, {-11, "POR"}, {-12, "USA"}, {-13,
"FRA"}, {-14, "CAN"}, {-15, "GER"}, {-16, "GRE"}},
{{-1, "1 BRA"}, {-2, "2 GBR"}, {-3, "3 POL"}, {-4, "4 SWE"}, {-5,
"5 NOR"}, {-6, "6 IRL"}, {-7, "7 SUI"}, {-8, "8 NZL"}, {-9,
"9 GER"}, {-10, "10 CAN"}, {-11, "11 DEN"}, {-12,
"12 CRO"}, {-13, "13 FRA"}, {-14, "14 USA"}, {-15,
"15 POR"}, {-16, "16 GRE"}}},
{{{0, "W1"}, {1, "L1"}, {2, "W2"}, {3, "F"}},
{{0, "W1"}, {1, "L1"}, {2, "W2"}, {3, "F"}}}},
PlotRange -> {Automatic, {-.7, -.3 - p}},
PlotStyle -> Thickness[.003]]
Here you have how to reformat your data with minimal manual effort.
tot = {
BRA = {03, 01, 01, 01}, CAN = {14, 14, 06, 10}, CRO = {10, 08, 11, 12},
DEN = {05, 05, 04, 11}, FRA = {13, 11, 14, 13}, GBR = {04, 02, 02, 02},
GER = {15, 15, 12, 09}, GRE = {16, 16, 16, 16}, IRL = {01, 03, 08, 06},
NOR = {02, 04, 09, 05}, NZL = {06, 06, 05, 08}, POL = {08, 07, 03, 03},
POR = {11, 13, 15, 15}, SUI = {07, 09, 10, 07}, SWE = {09, 10, 07, 04},
USA = {12, 12, 13, 14}}
then do
data = Ordering /# Transpose#tot
and using #DavidCarraher's answer in the question you mentioned.
Edit
If you have the timings for each team (for each lap), here is a skeleton for plotting a time scaled plot:
lapTimes = RandomReal[{0, 1}, {10, 5}];(*ten teams,five laps*)
ListLinePlot[Accumulate /# lapTimes, PlotRange -> {{1, 5}, All}]
The lower line at the end is the winner (less time consumed)

Resources