Rails - Iterate thru Multiple Arrays - ruby-on-rails

I'm looking for a more DRY way to iterate through some code. I have a User model and I want to keep count of certain Users in a ReportRecord model (for reporting).
I have a defined list of values of User.names that I want to record (i.e. "Jan", "Lisa", "Tina"). How can I make this code more DRY as the list is much longer than three values?
#users = User.all
#users.each do |u|
# this part repeats with different names
quantity = u.where("name = ?", "Jan").count
ReportRecord.create(:user_id => u.id, :name => "Jan", :quantity => quantity)
# repeated code with different name
quantity = u.where("name = ?", "Lisa").count
ReportRecord.create(:user_id => u.id, :name => "Lisa", :quantity => quantity)
# repeated code with different name
quantity = u.where("name = ?", "Tina").count
ReportRecord.create(:user_id => u.id, :name => "Tina", :quantity => quantity)
end

I would sum all users first (1 query instead of 3):
quantity_by_name = User.select(:name).where(name: %w(Jan List Tina))
.group(:name).sum(:quantity)
#=> { 'Lisa' => 1, 'Jan' => 2, 'Tina' => 3 }
quantity_by_name.each do |name, quantity|
ReportRecord.create(name: name, quantity: quantity)
end

names = %w(Jan List Tina)
names.each do |name|
count = User.where(name: name).count
ReportRecord.create(name: name, quantity: count) # I don't understand `u.id`
end

Related

How to Add has_many association field to array of models in Controller

I would like to add has_many models LoanAmtPerSvcType to LoanContract array.
Below is my code, but it does not work.
When i check #contracts[0].loan_amt_per_svc_type.count , it return '0'
#members.each do |c|
#contracts << LoanContract.new(
:customer_id => c.id,
:season_id => #season.id,
:loan_type_id => #loan_type.id,
:cus_group_id => #group.id,
contract_date: #contract_date,
loan_duration: #loan_duration,
inspector_id: #inspector.id,
mgr_id: #manager.id,
user_id: #user.id)
end
#contracts.each do |lc|
lc.loan_amt_per_svc_type = [LoanAmtPerSvcType.new(customer_service_type_id: 1), LoanAmtPerSvcType.new(customer_service_type_id: 2)]
end
render :text => #contracts[0].loan_amt_per_svc_type.count
#contracts[0].loan_amt_per_svc_type.count return 0, because you didn't save your contracts into the database.
You can use LoanContract.create instead of LoanContract.new. Also with associations.
If you only want to know count of loan_amt_per_svc_type use size method.
#contracts[0].loan_amt_per_svc_type.size

How to merge hash with activerecord relations in controller / scope

I have Item model(table) with column [id,name,notes]. then I have hash lets call it stock with column [id_of_item,total_stock],
when I do query in controller I would like to join the hash into the table as additional column so I can show the total_stock of the item.
I prefer not to use map/each (looping through all the items since the items table has thousand records. I still don't know whether this possibly or not, thank you.
if your stock is
[[1, "total_stock_1"], [2, "total_stock_2"]]
you should use
stock = Hash[[[1, "total_stock_1"], [2, "total_stock_2"]]]
to translate your hash to this style
stock = {1 => "total_stock_1", 2 => "total_stock_2"}
stock = {1 => "total_stock_1", 2 => "total_stock_2"}
#items = Item.all.map{|item| item.attributes.merge({total_stock: stock[item.id]})}
# the output will be a json not a ActiveRecordRelation
[
{:id => 1, :name => 'item1', :notes => xxx, :total_stock => "total_stock_1"},
{:id => 2, :name => 'item2', :notes => yyy, :total_stock => "total_stock_2"}
]
You can do this in controller:
#items = Item.all
render json: #items.map{|item| {'item': item.as_json.merge stock.select{|item| item['id_of_item'] == item.id}['total_stock']} }}

Best way to join two different model queries in Rails?

I have two different models that I need to join together in a selection query and list on a page. They share all of attributes that I'll need to reference in the view (created_at, updated_at, name, etc), and I want them in order of creation. I'm wondering what the most efficient way to do this is? I was thinking of performing the selection query on each object individually and adding the relevant parts into a common array but that seems inefficient.
For example if my models were Dogs and Cats and I wanted a list of all dogs and cats of age 5, I was thinking something like
#pets = []
dogs = Dogs.where(:age => 5)
cats = Cats.where(:age => 5)
dogs.each do |dog|
hash = {"id" => dog.id, "name" => dog.name, "created_at" => dog.created_at }
#pets.push(hash)
end
cats.each do |cat|
hash = {"id" => cat.id, "name" => cat.name, "created_at" => cat.created_at }
#pets.push(hash)
end
But is that the best way to do it? also, I'm not sure how to sort the finished array in this example according to date created...
try this
dogs = Dogs.where(:age => 5)
cats = Cats.where(:age => 5)
#pets = (dogs + cats).sort_by { |pet| pet.created_at }
OR if you want your hashes still, use map to create the array of hashes
dogs = Dogs.where(:age => 5)
cats = Cats.where(:age => 5)
#pets = (dogs + cats).sort_by do |pet|
pet.created_at
end.map do |pet|
{ "id" => dog.id, "name" => pet.name, "created_at" => pet.created_at }
end

Search ignoring certain blank parameters

I have a simple search action that has 3 parameters and a where method to search a model. If I search and some of the parameters are nil, it will not return the records I want. I want it to search the database by only using the parameters that are not nil/blank. So if only one category is entered and sent in the parameters, I want my controller to ignore the other two parameters. However, if the other parameters are present, I want them to be included in the search.
I've tried many approaches but I can't get it to work properly. Here's my code.
hash = []
cat = :category_id => params[:category_id]
col = :color_id => params[:color_id]
brand = :brand_id => params[:brand_id]
if params[:category_id].present?
hash += cat
end
if params[:color_id].present?
hash += col
end
if params[:brand_id].present?
hash += brand
end
#results = Piece.where(hash).preload(:item).preload(:user).group(:item_id).paginate(:page => params[:page], :per_page => 9)
I've put the variables into strings and hashs, called to_a, joined them with (","). Nothing works.
Thanks
Try this code.
criteria = { :category_id => params[:category_id], :color_id => params[:color_id],
:brand_id => params[:brand_id] }.select { |key,value| value.present? }
#results = Piece.where(criteria).preload(:item).preload(:user).group(:item_id).
paginate(:page => params[:page], :per_page => 9)

rails data aggregation

I have to create a hash of the form h[:bill] => ["Billy", "NA", 20, "PROJ_A"] by login where 20 is the cumulative number of hours reported by the login for all task transactions returned by the query where each login has multiple reported transactions. Did I do this in a bad way or this seems alright.
h = Hash.new
Task.find_each(:include => [:user], :joins => :user, :conditions => ["from_date >= ? AND from_date <= ? AND category = ?", Date.today - 30, Date.today + 30, 'PROJ1']) do |t|
h[t.login.intern] = [t.user.name, 'NA', h[t.login.intern].nil? ? (t.hrs_per_day * t.num_days) : h[t.login.intern][2] + (t.hrs_day * t.workdays), t.category]
end
Also if I have to aggregate this data not just by login but login and category how do I accomplish this?
thanks,
ash
I would make this
h[:bill] => ["Billy", "NA", 20, "PROJ_A"]
a hash like so
{ :user => t.user.name, :your_key_name => 'NA', :cumulative_hours => 20, :category => 'PROJ_A' }
so the values are accessible with keys instead of element indexes which becomes a bit hard to see when you are not iterating through a array
To access the data by user and category you can do some thing like this
user_hash = {}
Task.find_each(:include => [:user], :joins => :user, :conditions => ["from_date >= ? AND from_date <= ? AND category = ?", Date.today - 30, Date.today + 30, 'PROJ1']) do |task|
user_hash[task.login.intern] ||= {}
user_hash[task.login.intern][task.category] = { :user => task.user.name, :your_key_name => 'NA', :cumulative_hours => cumulative_hours(user_hash, task), :category => task.category }
end
def cumulative_hours(user_hash, task)
if user_hash[task.login.intern] && user_hash[task.login.intern][task.category]
return user_hash[task.login.intern][task.category][:cumulative_hours] + (task.hrs_day * task.workdays)
else
return task.hrs_per_day * task.num_days
end
end
For readability reasons I have added meaningful variable names and also created a method to calculate cumulative_hours to keep the code clear, separate code concern and to follow Single Responsibility Principle.

Resources