How to group rows based on multiple fields? - ruby-on-rails

I have a notifications table where I email out reports to people based on the frequency they've selected.
If an email address exists across multiple questions and has the same frequency...then I need to group them together so I can send one report for both questions.
[
#<Notification id: 1, question_id: 58, email: "john#example.com", frequency: "daily">,
#<Notification id: 2, question_id: 25, email: "john#example.com", frequency: "daily">,
#<Notification id: 3, question_id: 47, email: "john#example.com", frequency: "monthly">,
#<Notification id: 3, question_id: 19, email: "frank#example.org", frequency: "monthly">
]
So in my example data, 3 reports would be sent:
1 to john#example.com for question's 58 and 25 on a daily basis
1 to john#example.com for question 47 on a monthly basis
1 to frank#example.org for question 19 on a monthly basis
I may not be explaining this very well, so let me know if something needs clarification.

You can achieve this with a regular group_by:
#notifications = your_method_to_retrieve_notifications
#notifications = #noticications.group_by do |notif|
notif.ferquency + ':' + notif.email
end
This will group your notifications like this:
#notifications = {
'daily:john#example.com' => [<Notification id: 1>, #etc.],
'monthly:john#example.com' => [# list of notifications],
'monthly:frank#example.org' => [# list of notif]
}
If you want only an array of list of notifications grouped by frequency & email, use the above method and add this:
#notifications = your_method_to_retrieve_notifications
#notifications = #noticications.group_by do |notif|
notif.ferquency + ':' + notif.email
end.values
# returns Array of arrays like:
[
[<Notification id: 1 frequency: "daily", email "a#b.com">,<Notification id: 2 frequency: "daily", email "a#b.com">],
[<Notification id: 3 frequency: "daily", email "xx#yy.com">],
[<Notification id: 4 frequency: "monthly", email "a#b.com">,<Notification id: 5 frequency: "monthly", email "a#b.com">],
]

Related

Getting error on joins when trying to get its key.

Here's my stat model.
Stat(id: integer, points: float, user_id: integer, match_id: integer, team_id: integer)
For team model
Team(id: integer, name: string)
I'm getting error on teams.name part here's the error.
#<ActiveRecord::StatementInvalid: Mysql2::Error: Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'db-name.teams.name' which is not functionally dependent on columns in GROUP BY clause;
Sample stat:
{id: 1, points: 2, user_id: 1, match_id: 1, team_id: 1}
{id: 2, points: 3, user_id: 3, match_id: 1, team_id: 2}
{id: 3, points: 4, user_id: 1, match_id: 2, team_id: 1}
My current code:
sample = Stat
.joins(:user)
.joins(:team)
.select('teams.name as team_name, users.id as user_id, match_id, SUM(points) as points')
.where(user_id: params[:user_id])
.group(:match_id)
.where.not(match_id: nil)
.order("match_id DESC")
.limit(10)

"amount must be a Numeric" Rails Error

I'm using the active_shipping gem on my rails app and am getting a very strange error reading:
amount must be a Numeric
The error is being called on my shipping method:
def shipping
#user = current_user
#products = current_order.order_items.all
#order = current_order
packages = []
#products.each do |thing|
packages << ActiveShipping::Package.new( thing.product.weight * 16, <<<<<<<<<<ERROR CALLED ON THIS LINE
[thing.product.box_length, thing.product.box_width, thing.product.box_depth],
units: :imperial)
end ## each do
origin = ActiveShipping::Location.new( country: 'US', state: 'CO', city: 'Sedalia', zip: '80135')
if #user.country == 'US'
destination = ActiveShipping::Location.new( country: #user.country, state: #user.state, city: #user.city, zip: #user.zip)
else
destination = ActiveShipping::Location.new( country: #user.country, province: #user.state, city: #user.city, postal_code: #user.zip)
end # if/else for country
ups = ActiveShipping::UPS.new(login: 'lizbayardelle', password: 'UPSpassw0rd', key: '3D287D7B39D0D398')
ups_response = ups.find_rates(origin, destination, packages)
#ups_rates = ups_response.rates.sort_by(&:price).collect {|rate| [rate.service_name, rate.price]}
usps = ActiveShipping::USPS.new(login: '380LINCH6422')
usps_response = usps.find_rates(origin, destination, packages)
#usps_rates = usps_response.rates.sort_by(&:price).collect {|rate| [rate.service_name, rate.price]}
end
The value for the product weight is in the seeds.rb file:
Product.delete_all
Product.create! id: 1, name: "Rule #23 Tee Shirt (Small)", weight: 1, box_width: 6, box_length: 9, box_depth: 2, price: 32.00, short_description: "Size Small. Spread the word. These tee shirts not only have the Manly Art crest, but they also have a man rule on the back to remind any passers-by that may have forgotten.", active: true
Product.create! id: 2, name: "Rule #23 Tee Shirt (Medium)", weight: 1, box_width: 6, box_length: 9, box_depth: 2, price: 32.00, short_description: "Size Medium. Spread the word. These tee shirts not only have the Manly Art crest, but they also have a man rule on the back to remind any passers-by that may have forgotten.", active: true
Product.create! id: 3, name: "Rule #23 Tee Shirt (Large)", weight: 1, box_width: 6, box_length: 9, box_depth: 2, price: 32.00, short_description: "Size Large. Spread the word. These tee shirts not only have the Manly Art crest, but they also have a man rule on the back to remind any passers-by that may have forgotten.", active: true
Product.create! id: 4, name: "Rule #23 Tee Shirt (XL)", weight: 1, box_width: 12, box_length: 14, box_depth: 3, price: 32.00, short_description: "Size XL. Spread the word. These tee shirts not only have the Manly Art crest, but they also have a man rule on the back to remind any passers-by that may have forgotten.", active: true
Product.create! id: 5, name: "Grill Glove", weight: 1, box_width: 6, box_length: 9, box_depth: 2, price: 32.00, short_description: "Protect your manly mitts from the fiery dangers of the grill.", active: true
Product.create! id: 6, name: "BBQ Apron", weight: 1, box_width: 6, box_length: 9, box_depth: 2, price: 32.00, short_description: "Standard grilling apron with three pockets for all your necessary tools. You get to add the grease marks and stains from the marinade of your choosing.", active: true
Can anyone see what's going on here? Google/Stack Overflow don't seem to have heard of this error, which is worrisome...
I ended up adding .to_i to each thing and it made it work for some reason.

extract single cases from array

An array of records being generated by
#signatures = Signature.where('action_id IN (?)', #actions).all
will have actions with one or more signatures.
Signature id: 1, action_id: 1
Signature id: 2, action_id: 2
Signature id: 3, action_id: 1
Signature id: 4, action_id: 3
Signature id: 5, action_id: 2
What ruby instruction can extract just the single cases, based on action_id? Signature.id = 4 in the above example
#signatures = Signature.where(action_id: #actions)
.group('action_id')
.select('id, count(action_id)')
.having('count(action_id) = 1')

Given an object with multiple records, how to determine the count per field?

Given the rails object #messages, which contacts the following:
<Message id: 6, sender_id: 2, message: "123">
<Message id: 61, sender_id: 2, message: "stuff abc">
<Message id: 631, sender_id: 2, message: "hello world">
<Message id: 3, sender_id: 4, message: "hello world">
How can I determine the count per unique sender_id. Meaning outputting:
2 | 3
4 | 1
Thanks
you can use count
#messages.count(:id, group: :sender_id)
which will output a hash
{ 2 => 3, 4 => 1 }
if you need to output it like in your question
#messages.count(:id, group: :sender_id).each do |sender_id, count|
puts "#{sender_id} | #{count}"
end

Find Next and previous records ordered by position column rails

>> c = Course.find(3).course_steps.order(:position)
=> [#<CourseStep id: 9, step_id: 4, course_id: 3, position: 1, created_at: "2011-03-08 20:57:44", updated_at: "2011-03-08 20:57:44">, #<CourseStep id: 10, step_id: 5, course_id: 3, position: 2, created_at: "2011-03-08 20:57:45", updated_at: "2011-03-08 20:57:45">, #<CourseStep id: 8, step_id: 2, course_id: 3, position: 3, created_at: "2011-03-08 20:57:42", updated_at: "2011-03-08 20:57:42">]
I need to find a course_step that is after id 9 (which happens to be course_step with id 10) (if it is exists)
I also need to find the previous (if it is exists)
I know I could manually do it be looping through the results, but I would rather do it with SQL.
The NEXT sql query would be:
SELECT * FROM course_steps WHERE position >=POSITION_OF_STEP ORDER BY position LIMIT 1 OFFSET 1
The PREVIOUS sql query would be:
SELECT * FROM course_steps WHERE position <= POSITION_OF_STEP ORDER BY position DESC LIMIT 1 OFFSET 1
I think I got it!
class CourseStep < ActiveRecord::Base
belongs_to :step
belongs_to :course
validates_uniqueness_of :step_id, :scope => :course_id
def next_step()
Course.find(self.course.id).course_steps.order(:position).where("position >= ?", self.position).limit(1).offset(1).first
end
def previous_step()
Course.find(self.course.id).course_steps.order("position DESC").where("position <= ?", self.position).limit(1).offset(1).first
end
end

Resources