find start and end of range given a point - ruby

I created this basic class right here:
class TimePeriod
MONTHS_PER_QUARTER = 3
QUARTER_RANGE = {
0 => [1,3],
1 => [4,6],
2 => [7,9],
3 => [10,12]
}
def self.quarter(month_num)
(month_num - 1) / MONTHS_PER_QUARTER
end
def self.quarter_range(month_num)
quarter = quarter month_num
t1,t2 = QUARTER_RANGE[quarter]
[Time.parse(Date::MONTHNAMES[t1]).beginning_of_month, Time.parse(Date::MONTHNAMES[t2]).end_of_month]
end
end
It gives me a time range for a quarter, given a month provided as an integer:
> TimeUtils.quarter_range(Time.now.month)
=> [2015-04-01 00:00:00 -0400, 2015-06-30 23:59:59 -0400]
So it works. However, I have cheated. I had difficulty finding the start and end, given, let's say, the month 6. So I hardcoded the values in the QUARTER_RANGE constant. I want to be able to remove that QUARTER_RANGE constant and find the beginning and end (e.g. [4,6]) without it.
So for example, if the input 3 (March),6 (June),9(September),12(December) is passed, I will know its the end of the quarter, using modulus arithmetic:
3 % 3
=> 0
6 % 3
=> 0
9 % 3
=> 0
12 % 3
=> 0
But the tricky part is given let's say 5 (May), how can I return [4,6]?

You can get the start of the quarter like this:
def qtr_start(mon)
mon - (mon - 1) % MONTHS_PER_QUARTER
end
qtr_start(9) # => 7
The end of the quarter is just that plus two:
def qtr_end(mon)
qtr_start(mon) + 2
end
qtr_end(9) # => 9
Put them together:
def qtr_start_end(mon)
start = mon - (mon - 1) % MONTHS_PER_QUARTER
[ start, start + 2 ]
end
(1..12).each do |mon|
start_end = qtr_start_end(mon)
puts "Month #{mon} is in quarter #{start_end.inspect}"
end
# => Month 1 is in quarter [1, 3]
# Month 2 is in quarter [1, 3]
# Month 3 is in quarter [1, 3]
# Month 4 is in quarter [4, 6]
# Month 5 is in quarter [4, 6]
# Month 6 is in quarter [4, 6]
# Month 7 is in quarter [7, 9]
# Month 8 is in quarter [7, 9]
# Month 9 is in quarter [7, 9]
# Month 10 is in quarter [10, 12]
# Month 11 is in quarter [10, 12]
# Month 12 is in quarter [10, 12]

(1..12).each do |m|
low = 3*((m-1)/3) + 1
p m, [low, low+2]
end
result:
1
[1, 3]
2
[1, 3]
3
[1, 3]
4
[4, 6]
5
[4, 6]
6
...

Well, I'm not sure why you want it but if you want to use modulo this should work out how you want:
month = some_num
quarter_range = []
if month%3 == 0
# end of quarter
quarter_range = [month-2, month]
elsif month%3 == 1
# beginning of quarter
quarter_range = [month, month+2]
else
# middle of quarter
quarter_range = [month-1, month+1]
end
return quarter_range

You could do the following:
MONTHS_PER_PERIOD = 3
PERIODS = (1..12).step(MONTHS_PER_PERIOD).map do |m|
m..(m+MONTHS_PER_PERIOD-1)
end
#=> [1..3, 4..6, 7..9, 10..12]
def month_to_period(m)
PERIODS.find { |p| p.cover?(m) }
end
(1..12).each { |m| puts month_to_period(m) }
1..3
1..3
1..3
4..6
4..6
4..6
7..9
7..9
7..9
10..12
10..12
10..12
MONTHS_PER_PERIOD = 2
PERIODS = (1..12).step(MONTHS_PER_PERIOD).map do |m|
m..(m+MONTHS_PER_PERIOD-1)
end
#=> [1..2, 3..4, 5..6, 7..8, 9..10, 11..12]
(1..12).each { |m| puts month_to_period(m) }
1..2
1..2
3..4
3..4
5..6
5..6
7..8
7..8
9..10
9..10
11..12
11..12

Related

Finding remaining months in a fiscal year

I need to calculate the number of remaining months in a fiscal year from the given month, where a fiscal year starts from the fourth month of the year. I have an array illustrating this:
months = [4, 5, 6, 7, 8, 9, 10, 11, 12, 1, 2, 3]
Some expected inputs and outputs are:
4 => 11
10 => 5
2 => 1
However, when 3 is given (as well as when 2 is given), I want it to return 1 because the return value is used in a multiplication, and if it is 0, the output becomes 0.
I solved this by doing:
#month == 3 ?
annual_taxable_income += total_monthly_income_present_month :
annual_taxable_income += total_monthly_income_present_month + #basic_salary * fiscal_months_array[#month - 3, 12].size
I used ternary operator to check if there is 3, and if there is, then do not find the remaining months. The solution is working but I need a better way to do this.
def remaining_months(mon)
(3 - mon) % 12
end
(1..12).each { |mon| puts "#{mon} -> #{remaining_months(mon)}" }
1 -> 2
2 -> 1
3 -> 0
4 -> 11
5 -> 10
6 -> 9
7 -> 8
8 -> 7
9 -> 6
10 -> 5
11 -> 4
12 -> 3
You therefore would want [remaining_months(mon), 1].max. It's best not to put this part in the method remaining_months.
Note that the array months is determined by the first element, which is the first month of the fiscal year (4). It therefore should not be surprising that only the first element is needed in calculations.
Do you ever have problems like this where a modulus is involved and finding yourself trying different expressions until you finally find hit on one that works? Here's a way to do that for those in audience who are somewhat lazy by nature.
RESULT = [*(3..11), 0, 1, 2].reverse
a = [*(-12..0), *(1..12)]
a.product([1,-1], (a-[0]), a).
select do |c1,c2,c3,c4|
(1..12).all? { |mon| c1 + c2 * ((c3 * mon + c4) % 12) == RESULT[mon-1] }
end.
map do |c1,c2,c3,c4|
s1 = c1.zero? ? '' : c1.to_s
s2 = case c1.zero?
when true then c2==1 ? '' : '-'
else c2==1 ? ' + ' : ' - '
end
s3 = if c3 > 0
c3==1 ? '' : "#{c3} * "
else
c3==-1 ? '- ' : "- #{c3.abs} * "
end
s4 = case c4 <=> 0
when 0 then ''
when -1 then " - #{c4.abs}"
else " + #{c4}"
end
"#{s1}#{s2}((#{s3}mon#{s4}) % 12"
end
#=> ["((- mon - 9) % 12",
# "((- mon + 3) % 12",
# "(11 * mon - 9) % 12",
# "(11 * mon + 3) % 12",
# "11 - ((- 11 * mon - 4) % 12",
# "11 - ((- 11 * mon + 8) % 12",
# "11 - ((mon - 4) % 12",
# "11 - ((mon + 8) % 12"]
Take your pick!
Here we obtain the following intermediate result.
a.product([1,-1], (a-[0]), a).
select do |c1,c2,c3,c4|
(1..12).all? { |mon| c1 + c2 * ((c3 * mon + c4) % 12) == RESULT[mon-1] }
end
#=> [[0, 1, -1, -9], [0, 1, -1, 3], [0, 1, 11, -9], [0, 1, 11, 3],
# [11, -1, -11, -4], [11, -1, -11, 8], [11, -1, 1, -4], [11, -1, 1, 8]]
you could set the min value of the starting index value with max
fiscal_months = [4,5,6,7,8,9,10,11,12,1,2,3]
index = [#month-3, 1].max
annual_taxable_income += total_monthly_income_present_month + #basic_salary * fiscal_months[index, 12].size
It seems odd that you want a function that returns the same answer (1) for two different inputs (2,3). But, if that is the case you could do it like this. It is brute force but makes clear what you are trying to do:
def num_remaining_months_for (m)
fiscal_months = [4, 5, 6, 7, 8, 9, 10, 11, 12, 1, 2, 3]
remaining = [11, 10, 9, 8, 7, 6, 5, 4, 3 , 2, 1, 1]
index = fiscal_months.index(m)
remaining[index]
end
(1..12).each do |e|
puts "For month #{e} => #{num_remaining_months_for(e)} remaining"
end
The results are:
For month 1 => 2 remaining
For month 2 => 1 remaining
For month 3 => 1 remaining
For month 4 => 11 remaining
For month 5 => 10 remaining
For month 6 => 9 remaining
For month 7 => 8 remaining
For month 8 => 7 remaining
For month 9 => 6 remaining
For month 10 => 5 remaining
For month 11 => 4 remaining
For month 12 => 3 remaining
I would do it like that. I think it's clearer.
def number_of_remaining_months(month)
fiscal_months_array = [4, 5, 6, 7, 8, 9, 10, 11, 12, 1, 2, 3]
month = 2 if month == 3
fiscal_months_array.count - 1 - fiscal_months_array.index(month)
end
puts("Number of the month [1-12]:")
month = Integer(gets.chomp)
result = number_of_remaining_months(month)
annual_taxable_income = total_monthly_income_present_month +
#basic_salary * result
I hope it useful for you.

Changing the order of my code result in error ?

def stock_picker prices
min_day , max_day , profit = 0 , 0 , 0
i = 1
while i < prices.length
(0...i).each do |day|
if prices[i] - prices[day] > profit
min_day , max_day , profit = day , i , prices[i] - prices[day]
end
#i += 1
end
i += 1
end
return "[#{min_day}, #{max_day}]"
end
prices = [17,3,6,9,15,8,6,1,10]
puts stock_picker prices
My objective is to implement a method #stock_picker that takes in an array of stock prices, one for each hypothetical day. It should return a pair of days representing the best day to buy and the best day to sell. Days start at 0.
My question is why is it that this code wouldn't work if I remove line 11 and wrote it on line 9 instead. Which will then result in the error as follows :
**PS C:\Users\dlim\mystuff> ruby stockpicker.rb
stockpicker.rb:8:in `block in stock_picker': undefined method `-' for nil:NilClass (NoMethodError)
from stockpicker.rb:7:in `each'
from stockpicker.rb:7:in `stock_picker'
from stockpicker.rb:29:in `<main>'
You're basically trying to rewrite combination and max_by :
prices = [17, 3, 6, 9, 15, 8, 6, 1, 10]
days = (0...prices.size).to_a
p days.combination(2).max_by { |day1, day2| prices[day2] - prices[day1] }
# => [1,4]
If you want both the days and the corresponding prices :
[17,3,6,9,15,8,6,1,10].each.with_index.to_a.
combination(2).max_by{|(buy, day1), (sell, day2)|
sell-buy
}
# => [[3, 1], [15, 4]]
Where is this occurring?
Your error
stockpicker.rb:8:in `block in stock_picker': undefined method `-' for nil:NilClass (NoMethodError)
from stockpicker.rb:7:in `each'
from stockpicker.rb:7:in `stock_picker'
from stockpicker.rb:29:in `<main>'
Is occurring on 8th line
if prices[i] - prices[day] > profit
When it tries to access prices[i] when i = 9 and the prices returns nil,
which does not respond to the minus - operator.
Why is this occurring?
You are a loop within a loop
(0...i).each do |day|
if prices[i] - prices[day] > profit
min_day , max_day , profit = day , i , prices[i] - prices[day]
end
#i += 1
end
Increasing the i index counter variable here does not really make sense, since
the day is already iterating over the values in the Range (0...i) and
increasing i inside this loop means its comparing every value in the prices
array once against the next day value in the prices inner array, which
will only include the first three values of the prices (meaning the values at
the end of the prices array, like 1 and 10 will never be compared against
one another); e.g.
i = 1
prices = [17,3,6,9,15,8,6,1,10]
# iteration 0
if prices[i] - prices[day] > profit
# 3 - 17 > 0 # => false
# iteration 1
i += 1 # => 2
day # => 0
if prices[i] - prices[day] > profit
# 6 - 17 > 0 # => false
i += 1 # => 3
day # => 1
# iteration 2
if prices[i] - prices[day] > profit
# 9 - 3 > 0 # => true
min_day, max_day, profit = 1, 3, 6
i += 1 # => 4
day # => 0
# iteration 3
if prices[i] - prices[day] > profit
# 15 - 17 > 0 # => false
i += 1 # => 5
day # => 1
# iteration 4
if prices[i] - prices[day] > profit
# 8 - 3 > 0 # => true
min_day, max_day, profit = 1, 5, 5
i += 1 # => 6
day # => 2
# iteration 5
if prices[i] - prices[day] > profit
# 6 - 6 > 0 # => false
i += 1 # => 7
day # => 3
# iteration 6
if prices[i] - prices[day] > profit
# 1 - 9 > 0 # => false
i += 1 # => 8
day # => 0
# iteration 7
if prices[i] - prices[day] > profit
# 10 - 17 > 0 # => false
i += 1 # => 9
day # => 1
# iteration 8
if prices[i] - prices[day] > profit
# nil - 3 > 0 # => NoMethodError
At the 8th iteration, the outer loop has caused an out of bounds error when
accessing the prices array with prices[i], but is still iterating within the
second loop with the Range of (0...7) that was set after the 5th iteration, so
it is not reaching your while loop's escape clause/expression
while i < prices.length.
Possible solution:
You could keep your working solution or you could simplify your code by using
another Range as the outer loop
(1...prices.length).each do |i|
# ...
end
Instead of increasing an index counter variable within the while loop
i = 1
while i < prices.length
# ...
i +=1
end
Which would look like this
def stock_picker prices
min_day , max_day , profit = 0 , 0 , 0
(1...prices.length).each do |i|
(0...i).each do |day|
if prices[i] - prices[day] > profit
min_day , max_day , profit = day , i , prices[i] - prices[day]
end
end
end
return "[#{min_day}, #{max_day}]"
end
prices = [17,3,6,9,15,8,6,1,10]
puts stock_picker prices
And it will iterate over the following pairs of days, as per your requirements
[i, day]
# => [1, 0], [2, 0], [3, 0], [4, 0], [5, 0], [6, 0], [7, 0], [8, 0],
# [2, 1], [3, 1], [4, 1], [5, 1], [6, 1], [7, 1], [8, 1],
# [3, 2], [4, 2], [5, 2], [6, 2], [7, 2], [8, 2],
# [4, 3], [5, 3], [6, 3], [7, 3], [8, 3],
# [5, 4], [6, 4], [7, 4], [8, 4],
# [6, 5], [7, 5], [8, 5],
# [7, 6], [8, 6],
# [8, 7]
UPDATE:
You could also simplify it again with the Ruby Array combination method
(0...prices.length).to_a.combination(2)
To generate the same unique and non-duplicate pairs of days as iterating over the Ranges implied, which would look like this
def stock_picker prices
min_day , max_day , profit = 0 , 0 , 0
(0...prices.length).to_a.combination(2).each do |day, i|
if prices[i] - prices[day] > profit
min_day , max_day , profit = day , i , prices[i] - prices[day]
end
end
return "[#{min_day}, #{max_day}]"
end
prices = [17,3,6,9,15,8,6,1,10]
puts stock_picker prices
|day, i| will access the first and second variables in the array of day index pairs within array of combinations, whilst reusing the existing variable names you have used.

Ruby Arrays - Find the sums of the diagonals

Haven't seen this one before, but I was wondering how you can find the sums of both diagonals of a 2D array in Ruby. Say you have a simple array, with 3 rows and 3 columns.
array = [1,2,3,4,5,6,7,8,9]
I can break it into groups of three by using
array.each_slice(3).to_a
Would now be
[1,2,3], [4,5,6], [7,8,9]
[1,2,3]
[4,5,6]
[7,8,9]
In this case, the diagonals are
1 + 5 + 9 = 15
3 + 5 + 7 = 15
So the total sum would be 15 + 15 = 30
I was thinking I could do something like
diagonal_sum = 0
for i in 0..2
for j in 0..2
diagonal_sum += array[i][j]
end
end
Here is my try :
array = [1,2,3,4,5,6,7,8,9]
sliced = array.each_slice(3).to_a
# As sliced size is 3, I took 2, i.e. 3 - 1
(0..2).map { |i| sliced[i][i] } #=> [1, 5, 9]
(0..2).map { |i| sliced[i][-i-1] } # => [3, 5, 7]
(0..2).map { |i| sliced[i][i] }.reduce :+
# => 15
(0..2).map { |i| sliced[i][-i-1] }.reduce :+
# => 15
As per the above observation it seems in one iteration you can do solve :
left_diagonal, right_diagoal = (0..2).each_with_object([[], []]) do |i, a|
a[0] << sliced[i][i]
a[1] << sliced[i][-i-1]
end
left_diagonal.reduce(:+) # => 15
right_diagonal.reduce(:+) # => 15
Added, OOP style of code :
class SquareMatrix
attr_reader :array, :order
def initialize array, n
#array = array.each_slice(n).to_a
#order = n
end
def collect_both_diagonal_elements
(0...order).collect_concat { |i| [ array[i][i], array[i][-i-1] ] }
end
def collect_left_diagonal_elements
(0...order).collect { |i| array[i][i] }
end
def collect_right_diagonal_elements
(0...order).collect { |i| array[i][-i-1] }
end
def sum_of_diagonal_elements type
case type
when :all then collect_both_diagonal_elements.reduce(0, :+)
when :right then collect_right_diagonal_elements.reduce(0, :+)
when :left then collect_left_diagonal_elements.reduce(0, :+)
end
end
end
array = [1,2,3,4,5,6,7,8,9]
sqm = SquareMatrix.new array, 3
sqm.collect_both_diagonal_elements # => [1, 3, 5, 5, 9, 7]
sqm.sum_of_diagonal_elements :all # => 30
sqm.collect_left_diagonal_elements # => [1, 5, 9]
sqm.sum_of_diagonal_elements :left # => 15
sqm.collect_right_diagonal_elements # => [3, 5, 7]
sqm.sum_of_diagonal_elements :right # => 15
The following is mostly for the academic discussion:
For the main diagonal, you are looking for the "Trace" function which is defined for the "Matrix" class. So the following will work (although it doesn't get you the other diagonal and I wouldn't bet on its efficiency):
require 'Matrix'
a = array.each_slice(3).to_a
Matrix[*a].trace
To get the other diagonal you have to somehow "flip" the matrix, so the following seems to work (Since the result of each_slice is an array of rows, reverse reverses the order of the row. Reversing the order of the columns is more difficult):
Matrix[*a.reverse].trace
I totally forgot about #map.with_index ...Thanks to #xlembouras , heres a one-liner
first_diagonal = array.map.with_index {|row, i| row[i]} .inject :+
inverted_diagonal = array.map.with_index {|row, i| row[-i-1]} .inject :+
It's possible to make it a one-liner:
first_diagonal, inverted_diagonal = (array.map.with_index {|row, i| row[i]} .inject :+) , (array.map.with_index {|row, i| row[-i-1]} .inject :+)
Original:
Here's a thought, which makes me think it would be great to have a #map_with_index method:
for a first to last diagonal:
i = -1
array.map { |row| row[i=i+1] }.inject :+
for the last to first diagonal (assuming a square array):
i = array.length
array.map { |row| row[i=i-1] }.inject :+
a = [1,2,3,4,5,6,7,8,9]
p a.values_at(0,2,4,4,6,8).inject(&:+) #=> 30
I would try iterating through the array and keep the values that I need according to the length of the (grouped) array
array = [[1,2,3], [4,5,6], [7,8,9]]
dimension = array.length
array.flatten.map.with_index do |x,i|
x if [0, dimension - 1].include?(i % dimension)
end.compact.inject(:+)
#=> 30
You don't need to first apply slice:
arr = [1,2,3,4,5,6,7,8,9]
We visualize arr as:
1 2 3
4 5 6
7 8 9
n = Math.sqrt(arr.size).round
#=> 3
For the main diagonal:
(0...arr.size).step(n+1).reduce(0) { |t,i| t+arr[i] }
#=> 15
For the off-diagonal:
(n-1..arr.size-n).step(n-1).reduce(0) { |t,i| t+arr[i] }
#=> 15
Another example:
arr = [1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6]
1 2 3 4
5 6 7 8
9 0 1 2
3 4 5 6
n = Math.sqrt(arr.size).round
#=> 4
(0...arr.size).step(n+1).reduce(0) { |t,i| t+arr[i] } +
(n-1..arr.size-n).step(n-1).reduce(0) { |t,i| t+arr[i] }
#=> 14 + 14 => 28
require 'Matrix'
arr = [[1, 3, 4], [2, 5, 7], [6, 7, 8]]
diag1 = Matrix[*arr].tr
diag2 = Matrix[*arr.reverse].tr
def diagonal(array)
single=array.flatten
new=[]
i=array.length-1
while i < single.length-2
new << single[i]
i+=array.length-1
end
new.sum
end
p diagonal([
[1, 2, 3],
[4, 5, 6],
[7, 9, 8],
])
OUTPUT
15
That is for finding the sum of right diagonal of a 2D array

Building Insertion Sort. Not sure what I'm doing wrong

The goal is to see how insertion sort finds and inserts a number into a pre-sorted array. I'm supposed to pop the last number from the array and insert it into its correct chronological place in the array.
The code seems to work for the first example, but not the second. In the second example the correct answer seems to come in the fourth iteration even though there are integers in the array.
It feels like I need to break or return and stop looping somewhere, but I'm not sure where.
def insertionSort( ar)
count = ar.count
value = ar.pop
p ar << value if value >= ar.last
reversed = ar.reverse
ar.count.times do |index|
reversed.unshift(reversed.first) if reversed.count < count
if reversed[index + 1] > value
reversed[index] = reversed[index+1]
else
reversed[index] = value
end
puts reversed.reverse.join(' ')
end
end
ar = [2, 4, 6, 8, 3]
insertionSort( ar )
#=> 2 4 6 8 8
#=> 2 4 6 6 8
#=> 2 4 4 6 8
#=> 2 3 4 6 8
negatives = [-3, -6, 7, 8, 9, 5]
insertionSort( negatives )
#=> -3 -6 7 8 9 9
#=> -3 -6 7 8 8 9
#=> -3 -6 7 7 8 9
#=> -3 -6 5 7 8 9
#=> -3 5 5 7 8 9
I figured it out. I needed a break and a puts on the else statement.
def insertionSort( ar)
count = ar.count
value = ar.pop
p ar << value if value >= ar.last
reversed = ar.reverse
ar.count.times do |index|
reversed.unshift(reversed.first) if reversed.count < count
if reversed[index + 1] > value
reversed[index] = reversed[index+1]
puts reversed.reverse.join(' ')
elsif reversed[index + 1] == value
reversed.slice!(index)
puts reversed.reverse.join(' ')
break
else
reversed[index] = value
puts reversed.reverse.join(' ')
break
end
end
end
ar = [2, 4, 6, 8, 3]
insertionSort( ar )
negatives = [-3, -6, 7, 8, 9, 5]
insertionSort( negatives )

ruby get next value on each loop

Can I get the next value in an each loop?
(1..5).each do |i|
#store = i + (next value of i)
end
where the answer would be..
1 + 2 + 2 + 3 + 3 + 4 + 4 + 5 + 5 = 29
And also can I get the next of the next value?
From as early as Ruby 1.8.7, the Enumerable module has had a method each_cons that does almost exactly what you want:
each_cons(n) { ... } → nil
each_cons(n) → an_enumerator
Iterates the given block for each array of consecutive <n> elements. If no block is given, returns an enumerator.
e.g.:
(1..10).each_cons(3) { |a| p a }
# outputs below
[1, 2, 3]
[2, 3, 4]
[3, 4, 5]
[4, 5, 6]
[5, 6, 7]
[6, 7, 8]
[7, 8, 9]
[8, 9, 10]
The only problem is that it doesn't repeat the last element. But that's trivial to fix. Specifically, you want
store = 0
range = 1..5
range.each_cons(2) do |i, next_value_of_i|
store += i + next_value_of_i
end
store += range.end
p store # => 29
But you could also do this:
range = 1..5
result = range.each_cons(2).reduce(:+).reduce(:+) + range.end
p result # => 29
Alternatively, you may find the following to be more readable:
result = range.end + range.each_cons(2)
.reduce(:+)
.reduce(:+)
Like this:
range = 1..5
store = 0
range.each_with_index do |value, i|
next_value = range.to_a[i+1].nil? ? 0 : range.to_a[i+1]
store += value + next_value
end
p store # => 29
There may be better ways, but this works.
You can get the next of the next value like this:
range.to_a[i+2]
One approach that wouldn't use indexes is Enumerable#zip:
range = 11..15
store = 0 # This is horrible imperative programming
range.zip(range.to_a[1..-1], range.to_a[2..-1]) do |x, y, z|
# nil.to_i equals 0
store += [x, y, z].map(&:to_i).inject(:+)
end
store

Resources