Ruby how to find each value in hash greater than x? - ruby

I am bit new to Ruby and had a problem with learning hashes. I have hash which contains months and days inside this month. And I need to print each one that has 31 days.
months = {
"MAR" => 31,
'APR' => 30,
'MAY' => 31,
'JUN' => 30
}

You can use:
months.select { |_month, days| days > 30 }
This former gives you all results that fit the criteria (days > 30).
Here are the docs:
select
Once you've got the values you need, you can then print them to the console (or output however you'd like), e.g.
long_months = months.select { |_month, days| days > 30 }
long_months.each { |month, days| puts "#{month} has #{days} days" }
All that being said, assigning to a value before printing the result means two loops, whereas this can be achieved in one using a simple each:
months.each do |month, days|
puts("#{month} has #{days} days") if days > 30
end
That'll be more efficient as there's less churn involved :)

Check select method with a block
months.select{|key, value| value > 30}

Related

Given a date, how to find if it's the second or fourth Saturday of the month

In my Rails application, I need to find if a given date is the second or fourth Saturday of the month. What's the efficient way to do this? Is there a gem I can use?
Days 1 to 7 are week 0
Days 8 to 14 are week 1
Days 15 to 21 are week 2
Days 22 to 28 are week 3
To get the week id, we can calculate (date.day-1)/7. Since the id is zero-based, the second and fourth saturdays have an odd week id :
def second_or_fourth_saturday?(date)
date.saturday? && ((date.day - 1) / 7).odd?
end
The second Saturday has to be in the day range 8-14, and the fourth in the day range 22-28. So I think this should work
def second_or_forth_saturday?(date)
return false unless date.saturday?
(8..14).include?(date.day) || (22..28).include?(date.day)
end
def fourth_saturday?(date)
saturdays = (date.beginning_of_month..date.end_of_month).select { |date| date.wday == 6 }
[satudays.second, saturdays.fourth].include?(date)
end
create a month dates range
select Saturdays
see, if second or fourth Saturday is equal to date
As #Stefan kindly suggested, the first line of the method could be written as follows (using all_month):
saturdays = date.all_month.select(&:saturday?)
References:

multiple group by using linq

I need return just 2 lines in my query. One line with a string Today and a number of cases closed today, on my second line I need a string Last Week and a number of cases closed on the last week.
How I group with a range date?
Sum Name
----------- ----------
12 Today
33 Last Weeb
How about this:
var caseCounts = Cases
.Where(c => c.Date == today || (c.Date >= startOfLastWeek && c.Date <= endOfLastWeek))
.GroupBy(c => c.Date == today ? "Today" : "Last Week")
.Select(g => new {
Name = g.Key, Sum = g.Count()
});
You would need to define the 3 dates (today, startOfLastWeek, endOfLastWeek) before hand, but it gives you the results you are after.
GROUP BY YEARWEEK(date) should work. Depending on your dbms, you might be able to use another function, or program your own.
http://www.tutorialspoint.com/sql/sql-date-functions.htm#function_yearweek

Ruby Nested Hash with Composite Unique Keys

Given a comma separated CSV file in the following format:
Day,User,Requests,Page Views,Browse Time,Total Bytes,Bytes Received,Bytes Sent
"Jul 25, 2012","abc123",3,0,0,13855,3287,10568
"Jul 25, 2012","abc230",1,0,0,1192,331,861
"Jul 25, 2012",,7,0,0,10990,2288,8702
"Jul 24, 2012","123456",3,0,0,3530,770,2760
"Jul 24, 2012","abc123",19,1,30,85879,67791,18088
I wanted to drop the entire dataset (1000 users over 30 days = 30,000 records) into a hash such that Key 1 may be a duplicate key, key 2 may be a duplicate key, but Key 1 & 2 will be unique together.
Example using line 1 above:
report_hash = "Jul 25, 2012" => "abc123" => {"PageRequest" => 3, "PageViews" => 0, "BrowseTime" => 0, "TotalBytes" => 13855, "BytesReceived" => 3287, "BytesSent" => 10568}
def hashing(file)
#read the CSV file into an Array
report_arr = CSV.read(file)
#drop the header row
report_arr.drop(1)
#Create an empty hash to save the data to
report_hash = {}
#for each row in the array,
#if the first element in the array is not a key in the hash, make one
report_arr.each{|row|
if report_hash[row[0]].nil?
report_hash[row[0]] = Hash.new
#If the key exists, does the 2nd key exist? if not, make one
elsif report_hash[row[0]][row[1]].nil?
report_hash[row[0]][row[1]] = Hash.new
end
#throw all the other data into the 2-key hash
report_hash[row[0]][row[1]] = {"PageRequest" => row[2].to_i, "PageViews" => row[3].to_i, "BrowseTime" => row[4].to_i, "TotalBytes" => row[5].to_i, "BytesReceived" => row[6].to_i, "BytesSent" => row[7].to_i}
}
return report_hash
end
I spent several hours learning hashes and associated content to get this far, but feel like there is a much more efficient method to do this. Any suggestions on the proper/more efficient way of creating a nested hash with the first two keys being the first two elements of the array such that they create a "composite" unique key?
You could use the array [day, user] as the hash key.
report_hash = {
["Jul 25, 2012","abc123"] =>
{
"PageRequest" => 3,
"PageViews" => 0,
"BrowseTime" => 0,
"TotalBytes" => 13855,
"BytesReceived" => 3287,
"BytesSent" => 10568
}
}
You just have to make sure the date and user always appear the same. If your date (for example) appears in a different format sometimes, you'll have to normalize it before using it to read or write the hash.
A similar way would be to convert the day + user into a string, using some delimiter between them. But you have to be more careful that the delimiter doesn't appear in the day or the user.
EDIT:
Also make sure you don't modify the hash keys. Using arrays as keys makes this a very easy mistake to make. If you really wanted to, you could modify a copy using dup, like this:
new_key = report_hash.keys.first.dup
new_key[1] = 'another_user'

How to get sum in nested collection via LINQ

class YearlyData
{
MonthlyData[] monthlyData = new MonthlyData[12];
}
class MonthlyData
{
int Salary;
}
Given I have a List<YearlyData>, how can I find total salary for a given month for the number of years.
Example for three years I need total salary given for 1st month & subsequent months.
If you want the sum of all the Salaries for April:
List<YearlyData> l;
l.Sum(yd => yd.monthlyData[3].Salary);
If I misread your question, and you really want all the salaries for April & subsequent months (in the year)
l.Sum(yd => yd.monthlyData.Skip(3).Sum());
Something like this:
list.SelectMany(x => x.monthlyData
.Select((m, i) => new {month = i+1,
data = m.Salary}
))
.GroupBy(x => x.month)
.Select(x => new {month = x.Key,
total = x.Sum(m => m.data)});
This will give you a list with twelve entries, one for each month along with the total amount of that month.

Can someone explain/annotate this Ruby snippet with comments?

Please explain this Ruby code so I can convert it to PHP:
data = Hash.new({})
mysql_results.each { |r| data[r['year']][r['week']] = r['count'] }
(year_low..year_high).each do |year|
(1..52).each do |week|
puts "#{year} #{week} #{data[year][week]}"
end
end
data = Hash.new({})
# create hash 'data'
mysql_results.each { |r| data[r['year']][r['week']] = r['count'] }
# maps each row from sql query to hash like this: data[2010][30] = 23
# So you can access 'count' from every year and week in very simple way
(year_low..year_high).each do |year|
# for (year = year_low; year <= year_high; year++)
(1..52).each do |week|
# for (week = 1; week <=52; week++)
puts "#{year} #{week} #{data[year][week]}"
# printf("%d %d %d\n", year, week, data[year][week]);
end
end
Sorry for mixing C with pseudo code, but I hope it helps!
The first bit is just forming an array like so:
$data[2009][17] = 10;
PHP equivalent of that would be:
foreach ($mysql_results as $r){
$data[$r['year']][$r['week']] = $r['count'];
}
The second part would equate to the following:
foreach(range($year_low, $year_high) as $year){
foreach(range(1, 52) as $week){
print $year.' '.$week.' '.$data[$year][$week]
}
}
Hope that helps :)
$data = array();
#Build an array of 'count' per year/week
foreach($mysql_results as $r) {
$data[$r['year']][$r['week']] = $r['count'];
}
#Loop through the $data variable, printing out the 'count' for each year in the array,
#and all 52 weeks that year
for($year = $year_min; $year <= $year_max; $year++) {
for($week=1; $week<=52; $week++) {
echo "$year $week {$data[$year][$week]}";
}
}
Note that year_low and year_high are variables undefined in the current snippet, but they should be known to you.
Also, $mysql_results should be an array containing all rows returned by the database.
In short, the following code does this:
Make an array grouped per year, then per week, containing the value 'count'
Loop through this array, displaying, in order, the year, the week, and the value for 'count', if any

Resources