Removing form object hash while mapping through hash in Rails 5 - ruby-on-rails

I have a hash with products.
products = Product.all
I want to map through the hash and remove an object from the hash if a certain conditions is met.
products.map do |product|
product.delete if product.color == red
end
But this removes the object from the database. I only want to remove it from the products hash.
This is a simplified example where I could use SQL where statements. But in my real example this is more complex.

You can use Array#reject:
products.reject { |product| product.color == 'red' }
Or it's opposite Array#select:
products.select { |product| product.color != 'red' }

Related

How to modify an attribute of a certain item in a model

I have a model called Category which contains categories that are used to categorise products. One of the categories is called Experiences. On a specific query, I'd like to modify it from Experiences to Experiences * for display purposes.
I have tried to do this via a map, but am getting a blank value for Experiences.
This is my code:
def self.with_exp_star
Category.all.map { |element|
if element.name == "Experiences"
element.name = "Experiences *"
else
element
end
}
end
Any ideas?
Well, when element.name == "Experiences" you are pushing element.name to the result array, but element object otherwise, while you should return element in both ways:
Category.all.map { |element|
element.name += ' *' if element.name == 'Experiences'
element
}
If you're using rails 3 upwards you can chain a query with update_all, like so:
Categories.where(:name => "Experiences).update_all(:name => "Experiences *")

Put specific collection/array item on the last position in rails

I have a collection/array in rails, transformed to json it looks like this:
#collection = [{"order_number":"123","item":"Paper"},{"order_number":"567","item":"Ruler"},{"order_number":"344","item":"Pen"},{"order_number":"342","item":"Pencil"},{"order_number":"877","item":"Keyboard"}]
I would like to pick the item with the order_number "342" and put it at the last position of the collection, so the new collection looks like this:
#collection = [{"order_number":"123","item":"Paper"},{"order_number":"567","item":"Ruler"},{"order_number":"344","item":"Pen"},{"order_number":"877","item":"Keyboard"},{"order_number":"342","item":"Pencil"}]
In theory, it would look like this:
#collection.last = #collection[3]
but that is obviously not fancy ruby style nor would it re-sort the array as in my example.
Also I don't know the index of the item as it can change depending on what the user shops.
how about:
#collection << #collection.delete_at[#collection.index{|x| x[:order_number] == "342"}]
This basically searches the index of element with :order_number 342 first, uses that index to delete it, and then store the deleted element at the end again.
You can also use the partition method:
#collection = #collection.partition { |h| h['order_number'] != '342' }.flatten
Just split your collection on two (without 342 order and with 342 order), then just join them. It should looks like:
#collection = #collection.select {|e| e[:order_number] != '342' } + #collection.select {|e| e[:order_number] == '342' }
If you have an index of an item it boils down to
#collection << #collection.delete_at(3)
If you don't, you could try finding it using
#collection.find_index{ |el| el["order_number"] == "123" }
Alternative you can try this too:
> #collection.each_with_index{ |key,value| #collection.push(#collection.delete_at(value)) if key[:order_number] == "344" }
#=>[{:order_number=>"123", :item=>"Paper"}, {:order_number=>"567", :item=>"Ruler"}, {:order_number=>"342", :item=>"Pencil"}, {:order_number=>"877", :item=>"Keyboard"}, {:order_number=>"344", :item=>"Pen"}]

Rails - Reducing queries using includes

This is my controller:
#trainings = Training.includes(:courses).order(:id, 'courses.order_id ASC')
In my view, for each training I need to loop for 3 times and check if has a course with column order_id with these values:
#trainings.each do |training|
for course_order in (1..3) do
this_course = training.courses.find_by(order_id: course_order)
# this_course.image(:resized) #i can print the paperclip image
end
end
The code above executes so many queries, so I tried using select method:
#trainings.each do |training|
for course_order in (1..3) do
this_course = training.courses.select { |course| course.order_id = course_order }
# this_course.image(:resized) #I cannot print paperclip image, because the result is an Array, so doesn't know the method "image"
end
end
So I have just one query, but I cannot call the image method from my Model, because the result is an Array object.
I know I can use:
training.courses.each do |course|
# course.image(:resized)
end
and I will get just one query, but I must loop 3 times, so when I dont have the row I can print a placeholder image, like:
Example image http://www.onrails.com.br/order_id.jpg
Your
this_course = training.courses.find_by(order_id: course_order)
returns first record that matches conditions, but
this_course = training.courses.select { |course| course.order_id == course_order }
selects all courses that match condition. If you want to find only the first one, use .detect instead of .select:
this_course = training.courses.detect { |course| course.order_id == course_order }
Also you have a typo. = is for assignment, == is for comparing.

Handling a nil value from an instance method in Rails

I have a Product model which has many Items. The application lists unique items which belong to a product. So think of items as inventory. The following query grabs featured items for a product and removes the first item (irrelevant, but it becomes a featured item, displayed separately, if you're curious).
# product.rb
has_many :items_in_stock, -> { Item.in_stock }, class_name: 'Item'
def featured_items
items_in_stock.select("DISTINCT ON (condition) id, items.*")
.order(:condition, :price)
.sort_by { |item| item[:price] }[1..-1]
end
# item.rb
scope :in_stock, -> { where(status: 'in_stock') }
The trouble is when the feaured_items are empty, the method returns nil, and not a relation object. This means I get an error if I call #product.featured_items.any? on a product that has no items. If I remove the sort_by block, I get an empty relation object.
Is there a good way to handle this other than:
items = items_in_stock.select("DISTINCT ON (condition) id, items.*").order(:condition, :price)
if items.any?
items.sort_by { |item| item[:price] }[1..-1]
end
I can't reverse the ordering of the query because I get an error saying the order of the conditions in the order by statement must match the group conditions.
I'm confused...why call .any? on it then since nil is treated as false in ruby. If what you get back is nil then you know that you don't have any featured_items.
I ran this in irb and I think your issue is the [1..-1].
a = []
# => []
a.sort_by { |w| w.length }
# => []
a.sort_by { |w| w.length }[1..-1]
# => nil
The easiest way is to just do
items = items_in_stock.select("DISTINCT ON (condition) id, items.*")
.order(:condition, :price)
.sort_by { |item| item[:price] }
items.any? ? items[1..-1] : items
Then you don't actually have to do a check in other parts of your code unless it's necessary.
instead of if items.any? you can use unless items.blank? if it's nil or empty, it won't run the condition
items.blank? checks both items.empty? and items.nil?
And of course you can use it in your featured_items
items = items_in_stock.select("DISTINCT ON (condition) id, items.*")
.order(:condition, :price)
.sort_by { |item| item[:price] }[1..-1]
return Array.new if items.blank?
That way you know that result will be an array, no matter what
And for the proof, you can use .blank? on a nil object, and it works on nil itself, nil.blank? returns true

Parsing an array of objects by attributes in rails 3.2

Is it possible to parse an array of objects to select them by attribute? I have a situation where I need to display all objects of a model grouped by an attribute on the index page. What I had been doing in my controller is this...
#xx_controller.rb
#group1 = City.where(:population => 'big')
#group2 = City.where(:population => 'medium')
#group3 = City.where(:population => 'small')
But I'd prefer to do something like this in the controller...
#cities = City.all
And in my view something along the lines of a query, rather than prepackaged instance variables -
#cities.where....
Any thoughts?
If you don't mind loading everything at once from the database, you can do:
#cities = City.all.group_by(&:population)
Which returns a hash whose keys are the possible values for the population attribute.
Then, on your view, you can access the cities on each 'group' by doing #cities['small'], #cities['medium'] and so on.
Do you mean something like this?
#cities = City.all
small_cities = #cities.select { |city| city.population == 'small' }
medium_cities = #cities.select { |city| city.population == 'medium' }
big_cities = #cities.select { | city| city.population == 'big' }

Resources