Rails update attribute table col1 with col2 - ruby-on-rails

I want to (mass)update a model. Each line of should be updated by replacing the val of attb 1 with the val of attr2
users = User.find_all_by_project_id(params[:project_id])
users.each do |d|
User.update_attribute attribute1 with attribute2
end
How is the best way?
Thanks for your input.

This is how you should do it so everything happens on the sql side.
User.update_all("attribute1 = attribute2", { project_id: params[:project_id] })

That should be done this way:
User.where(:project_id => params[:project_id]).update_all('col1 = col2')
or
User.update_all('col1 = col2', {:project_id => params['project_id'] })

Related

Ruby - Extract value of a particular key from array of hashes

I have an array of hashes - #profiles which has data as:
[{:user_id=>5, :full_name=>"Emily Spot"},{:user_id=>7, :full_name=>"Kevin Walls"}]
I want to get full_name of say user_id = 7? I'm doing the following: but it's throwing an error that expression #profiles.find{|h| h[':user_id'] == current_user.id} is nil.
name = #profiles.find{ |h| h[':user_id'] == current_user.id }[':full_name']
if I use select instead of find then error is - no implicit conversion of String into Integer.
How do I search through the array of hashes?
UPDATE:
After #Eric's answer, I restructured my job model & view actions:
def full_names
profile_arr||= []
profile_arr = self.applications.pluck(:user_id)
#profiles = Profile.where(:user_id => profile_arr).select([:user_id, :first_name, :last_name]).map {|e| {user_id: e.user_id, full_name: e.full_name} }
#full_names = #profiles.each_with_object({}) do |profile, names|
names[profile[:user_id]] = profile[:full_name]
end
end
In the view....,
p #current_job.full_names[current_user.id]
#profiles is an array of hashes, with symbols as keys, whereas what you use is String objects.
So ':user_id' is a string, and you want symbol: :user_id:
#profiles.find{ |h| h[:user_id] == current_user.id }
I want to get full_name of say user_id == 7
#profiles.find { |hash| hash[:user_id] == 7 }.fetch(:full_name, nil)
Note, I used Hash#fetch for case, when there is no hash with value 7 at key :user_id.
As you've noticed, it's not very convenient to extract the name of user_id 7. You could modify your data structure a bit :
#profiles = [{:user_id=>5, :full_name=>"Emily Spot"},
{:user_id=>7, :full_name=>"Kevin Walls"}]
#full_names = #profiles.each_with_object({}) do |profile, names|
names[profile[:user_id]] = profile[:full_name]
end
p #full_names
# {5=>"Emily Spot", 7=>"Kevin Walls"}
p #full_names[7]
# "Kevin Walls"
p #full_names[6]
# nil
You didn't lose any information but name look-up is now much faster, easier and more robust.
Suggesting, to create a new hash that can make things simpler
Eg:
results = {}
profiles = [
{user_id: 5, full_name: "Emily Spot"},
{user_id: 7, full_name: "Kevin Walls"}
]
profiles.each do |details|
results[details[:user_id]] = details[:full_name]
end
Now, results will have:
{5: "Emily Spot", 7: "Kevin Walls"}
So, if you need to get full_name of say user_id = 7, simply do:
results[7] # will give "Kevin Walls"

Convert group count from activerecord into hash with multiple group stages

I'm trying to get some statistics.
Model.where(status:#statuses).group(:sub_group, :status).count
This returns me
{
["sub_group1", "status1"] => 3},
["sub_group2", "status3"] => 7,
["sub_group1", "status2"] => 5, ....etc }
I want to merge them so each element has a unique subgroup.
e.g. a hash like:
{
"sub_group1" => {"status1" => 3, "status2" => 5,
"sub_group2" => {"status3" => 7},
}
or an array
[
["subgroup1", {"status1" = 3, "status2" => 5}],
["subgroup2".....
]
i.e. I want all those terms to be merged with sub_group as the primary header so that I can get the results for subgroup in one item.
Brain not working today....
You can try with merge!:
result = Model.where(status: #statuses).group(:sub_group, :status).count
result.reduce({}) do |collection, (attributes, count)|
collection.merge!(attributes[0] => { attributes[1] => count }) do |_, prev_value, next_value|
prev_value.merge!(next_value)
end
end
Demonstration
I suppose you could do something like this:
res = Model.where(status:#statuses).group(:sub_group, :status).count
nice_hash = {}
res.each do |key, count|
nice_hash[key[0]] = {} unless nice_hash[key[0]]
nice_hash[key[0]][key] = count
end
After this nice_hash should be on the desired format
Please try with below code.
a = Model.where(status:#statuses).group(:sub_group, :status).count
res = {}
a.each do |k,v|
c={}
if res.has_key?(k[0])
c[k[1]]=v
res[k[0]]=res[k[0]].merge(c)
else
c[k[1]]=v
res[k[0]]=c
end
end
I believe the answer to your question should be this or this should guide in the right direction:
Model.where(status:#statuses).group_by(&:sub_group)
If you need the only the statuses as you mentioned, you could do:
Model.where(status:#statuses).select(:status, :sub_group).group_by(&:sub_group)

Rails find_by_name on another table

I have Genders and based on Gender name create category and subcategories.
m = Gender.create(:gender => 'masculine')
c = Category.find_by_name("T-shirt", gender: m )
c.subcategories.create(:name => "Necklace" )
and so on.
While the answer of Amit Sharma works I'd suggest have several improvements for it.
Use the new Hash syntax:
gender = Gender.create!(gender: 'masculine')
Use find_by instead of where/first
category = Category.find_by(gender: gender, name: 'T-Shirt')
Use the bang variants when not checking the return value
category.subcategories.create!(name: 'Necklace')
Use if/present? instead of unless/blank?
if category.present?
category.subcategories.create!(name: 'Necklace')
end
(this is just a matter of taste. But my brain seems to have troubles parsing unless expressions:-))
Use find_or_initialize_by/find_or_create_by!
If you want to find the category OR create it if it does not exist, use find_or_initialize/find_or_create_by!() so you can avoid the nil check:
category = Category.find_or_create_by!(gender: gender, name: 'T-Shirt')
So in total i'd write it like:
gender = Gender.create!(gender: 'masculine')
category = Category.find_or_create_by!(gender: gender, name: 'T-Shirt')
category.subcategories.create!(name: 'Necklace')
You can try this.
m = Gender.create(:gender => 'masculine')
c = Category.where(name: "T-shirt", gender: m.gender ).first
c.subcategories.create(name: "Necklace" )
Please note above code will raise an exception if no category found with given condition, so to avoid that you can use following.
m = Gender.create(:gender => 'masculine')
c = Category.where(name: "T-shirt", gender: m.gender).try(:first)
unless c.blank?
c.subcategories.create(name: "Necklace" )
end

Rails 3 ActiveRecord: "OR-ing" together multiple bools

I have an ActiveRecord object RaceCarDriver. Three of the fields are boolean: is_from_texas, is_from_arkansas, and is_from_indiana. In the user search interface, the user could select neither to see all results, select is_from_texas to see only drivers from Texas, or select is_from_texas and is_from_indiana to see all drivers from one of those states.
Now, I know this example is a bit contrived, but I wanted to avoid the complexity of the actual app.
My best attempt is along these lines:
#drivers = RaceCarDriver.select('name').
where(params[:check_texas] == 0 || :is_from_texas => params[:check_texas]).
where(params[:check_arkansas] == 0 || :is_from_arkansas => params[:check_arkansas]).
where(params[:check_indiana] == 0 || :is_from_indiana => params[:check_indiana])
However, this ANDS the chained where clauses together, making it so that if Texas and Indiana were both checked a driver would have to be from both states.
Again, I know this is contrived. Any help is appreciated.
RaceCarDriver.select('name').
where("is_from_texas = ? OR is_from_arkansas = ? OR is_from_indiana = ?",params[:is_from_texas],params[:is_from_arkansas],params[:is_from_indiana])
#drivers = RaceCarDriver.select('name')
#drivers = #drivers.where(:is_from_texas => params[:check_texas]) if params[:check_texas]
#drivers = #drivers.where(:is_from_arkansas => params[:check_arkansas]) if params[:check_arkansas]
#drivers = #drivers.where(:is_from_indiana => params[:check_indiana]) if params[:check_indiana]
EDIT
Solution 1:
where_options = { :is_from_texas => params[:check_texas],
:is_from_arkansas => params[:check_arkansas],
:is_from_indiana => params[:check_indiana] }.select{|k,v| v.present? }
where_conditions = where_options.map{|k,v| "#{k} = #{v}"}.join(" OR ")
#drivers = RaceCarDriver.select('name').where(where_conditions)
Solution 2:
#scoped_drivers = RaceCarDriver.select('name')
#drivers = []
#drivers << #scoped_drivers.where(:is_from_texas => params[:check_texas]) if params[:check_texas]
#drivers << #scoped_drivers.where(:is_from_arkansas => params[:check_arkansas]) if params[:check_arkansas]
#drivers << #scoped_drivers.where(:is_from_indiana => params[:check_indiana]) if params[:check_indiana]
#drivers.flatten!

How to have condition in ActiveRecord update

I am trying to update a record. Am using the following code.
Proddiscount.update({:prodid => params[:id]}, {:discount=>params[:discount]})
Query Im trying to have is:
update proddiscount set discount = '' where prodid = ''
If prodif is not a primary key, use update_all (read more here)
Proddiscount.update_all("discount = #{params[:discount]}", ["prodid = ?", params[:prodid])
But it won't trigger validations.
How about doing this:
#prod = Proddiscount.find(:first, :conditions => {prodid => params[:id]})
#prod.update_attributes({:discount=>params[:discount]})

Resources