Ruby 1.9.2 / Rails 3 - Summation from a hash collection - ruby

I'm having some difficulties creating a sum from values I have in a collection. The collection I have is from (in rails) a one to many relationship, an order has many products.
The products are hashes and have a price: value.
I've used the built in array.sum method before, so I was trying to grab all the prices from my products, and create a new array, and sum that, but have been unable to select just the price values from my products for this array.
Is there a better way I should be attempting to do this? I'm trying to build a method for my Order model that defines the order's total price from taking the sum of the product prices that belong to the order.

I would do the following:
class Order
has_many :products
def price
products.all.sum(&:price)
end
end
Now calling .price on an Order object will sum all product prices for you.

Related

Ruby / Redmine: compile a hash from issue subjects and appropriate custom fields

I’m using Redmine and Computed Custom Field plugin.
The plugin provides a possibility to make custom fields computed and it accepts ruby code for calculations.
In Redmine I have a project (Project_id = 11) where in I calculate the cost of products in a separate custom field for each issue. It looks like this:
Each Issue has a custom field (cf_id = 31) for selecting the product: Pears, Pineapples, Tomatoes, Coconuts.
Each Issue has a custom field (cf_id = 32) for entering the quantity (pieces) of goods.
Each Issue has a custom field (cf_id = 33) for entering the weight (pounds) of goods.
Each Issue has a computed custom field (cf_id = 34) in which the formula calculates the cost of the product.
The formula in the computed custom field (cf_id = 34) includes two hashes with prices of products (depending on the product type):
products_by_weight = {
"Pears" => [110],
"Tomatoes" => [120]
}
products_by_pieces = {
"Pineapples" => [130,300],
"Coconuts" => [140,200]
}
Then my formula checks the product selected in cf_id = 31 for belonging to the first or the second hashes and performs the corresponding calculations:
Multiplies the price by weight (cf_id = 32) in case of using the goods from the first list
Or multiplies the price by the quantity (cf_id = 33) in case of using the goods from the second list. The second value in the value array of "products_by_pieces" hash is the weight limit per piece. If the weight divided by the limit is a larger amount than entered in cf_id = 32, then the formula in scenario 2 will use this quantity instead of the one indicated in cf_id = 32.
Now I`m trying to move these variables outside the formula. I made a project (Project_id = 22) in which I want to save these variables as issues.
I imagine it like this:
The name of the issue is the name of the product
Each issue has two custom fields:
cf_id = 41 is price of product
cf_id = 42 is weight limit per piece
For each issue a category is assigned: "products_by_weight" or "products_by_pieces".
I want to compile the same hashes that are now included in my formula in the cf_id = 34 of the issues of the project 11, but automatically from the issues of the project 22, taking into account the category.
So far, all I have achieved is to find the price of a known product from such issues of the project 22.
price = Project.find(22).issues.where(subject: "Pineapples").first.try(:custom_field_value,41)
But this does not help in any way and requires changes to the code when adding each new product.
I'm new to programming and Ruby, so I’m trying to experiment with Redmine classes, and tried to compile a hash with such code:
Issue.by_category(Project.find(22))
But as a result, so far I have received only this:
[{"status_id"=>"27", "closed"=>true, "category_id"=>"1", "total"=>"10"}]
Which is completely different from the result I expect.
Any help would be helpful!
UPD.
Right now, my variables (product prices and weight limits) are in a hash, which is directly a part of the code for computed field 34. But I do not want these variables (prices and weight limits) to be part of the code. I want to manage them as Issues with corresponding custom fields (41 and 42) in a separate project (22) - in such a way that regular user can change or add these values in Issues without having to change the code of the calculated custom field (34). So I want to compile that hash based on the Issues from Project 22 instead of writing it directly. I assume this is so, that the Subjects of the Issues of the Project 22 should become the keys and array of custom fields [41,41] - the values. In doing so, I need two separate hashes determined by the assigned category ("goods_by_weight" and "goods_by_pieces") because they are calculated differently and in Project 22 I have other variables written as values of custom fields in Issues with a different category.
I solved this problem in the following way.
As planned, now I store the price list for my products as issues in a separate Project (ID 22). To get the prices hash of all products of the selected category (A) in the computed custom field's formula if Issue of Project (ID 11), I do the following:
PRICELIST_PROJECT_ID = 22
CATEGORY_A_ID = 1
PRICE_VALUE_CFID = 41
WLIMIT_VALUE_CFID = 42
delimiter = ','
pricelist_issues_cat_a = Project.find(PRICELIST_PROJECT_ID).issues.select { |rate| rate.category_id == CATEGORY_A_ID }
cata_products_names = []
cata_products_pvalues = []
for i in (0..pricelist_issues_cat_a.size-1) do
cata_products_names[i] = pricelist_issues_cat_a[i].try(:subject)
cata_products_pvalues[i] = pricelist_issues_cat_a[i].try(:custom_field_value,PRICE_VALUE_CFID).split(Regexp.union(delimiter)).map(&:to_f)
end
cata_price_hash = Hash[cata_products_names.zip(cata_products_pvalues)]
And the same way for product category B.
Not sure if this is the most efficient way, but it works for me.

Laravel Eloquent - Sum of a column values for multiple records from related data

I have the following models: User, Order, OrderPayment
whereby each user has many orders, and each order has many order payments.
The orderPayment model has the attribute "total_paid"
I would like to get the sum of the user's total paid for all his orders.
eg:
user has 3 orders.
the first order has the two following payment records: 5$ and 4$.
the second order has one payment of 10$
the third order has two payment records of 1$ and 4$
the total sum i want is 5 + 4+ 10+ 1+ 4 = 24$.
I have tried the following but it's not working at all :
$user->orders->orderpayment->sum('total_paid');
but i get this error
Property [orderPayment] does not exist on this collection instance
Since you want to sum values from the OrderPayment model, it is easier to start there. Try to write it like this:
OrderPayment::whereHas('order.user', function($query) use ($userId) {
$query->whereId($userId);
})->sum('total_paid');
Make sure all the relations are defined well.
Try:
$user->orders->orderpayment()->sum('total_paid');

Rails 4 + ActiveRecord: How to use .group to return an array of records and not just a single one?

I have a Product model that has_many ProductImages.
Each of those images has an attribute called name.
ex:
Product: T-shirt
T-shirt has images: image1 name:blue, image2 name:blue, image3 name:green
Is there a method in ActiveRecord to group all of T-shirt's images into an array of arrays based on name? I want something that would return [[image1, image2], [image3]], since image1 and image2 have the same color they are grouped into one array.
The docs (http://guides.rubyonrails.org/active_record_querying.html#group) say .group(:name) wouldn't do what I want: "this will give you a single Order object for each date where there are orders in the database."
Based on the output you want, you may want to check out the Ruby Enumerable group_by method. That will return a Hash and then you can get the values:
product_id = <T-Shirt Product ID Here>
ProductImage.where(product_id: product_id).group_by(&:name).values
This will give an array of arrays where the ProductImages are grouped together in the inner arrays by name.

How to merge ActiveRecord::Relation objects and add only the value, not the key

I have a simple question about programming in Ruby. I'm a newbie to Ruby, so if somebody can help me, I will really appreciate it.
Assume a system lets users have buyer and seller feedback ratings. I want to add/merge the buy and sell feedback ratings for a user into one consolidated rating, so only the rating needs to be added from the two Relation objects. The user id is only used as the key, but is not added.
buy_rating = user_object.group(buy_feedback_rating).select('buy_feedback_rating, COUNT(id) as count')
sell_rating = user_object.group(sell_feedback_rating).select('sell_feedback_rating, COUNT(id) as count')
buy_rating and sell_rating are histograms of the user's buy/sell rating, with 1=Terrible, 2=Poor, 3=Average, 4=Good, 5=Very Good.
The following is a sample array with (key,value) pairs where key=rating from 1 to 5, and value=number of ratings
buy rating = [(1,2),(2,5),(3,1),(4,7),(5,6)]
sell rating = [(1,3),(2,2),(3,7),(4,4),(5,7)]
Desired output = [(1,5),(2,7),(3,8),(4,11),(5,13)]
(obtained by adding only the second values from each array, not the first values).
The buy_rating and sell_rating arrays will only have the the key->value pair if the value>0. Meaning, if a buyer has no buyer rating=1, then the pair (1,0) will not be present in the buy_rating array. This means the arrays could be as follows:
buy_rating = [[2,5],[3,1],[4,7]]
sell_rating = [[1,3],[2,2],[5,7]]
Question is, how do I achieve the desired result? I want to add only the second column, not the first, from each array. Object returned should be of the same data type as buy_rating and sell_rating, i.e. buy_rating and sell_rating are both ActiveRecord::Relation objects, and the result should also be an ActiveRecord::Relation object.
You can make a map of values, sum based on the first, index, and then convert back to an array
buy_rating = [[1,2],[2,5],[3,1],[4,7],[5,6]]
sell_rating = [[1,3],[2,2],[3,7],[4,4],[5,7]]
merged_ratings = buy_rating + sell_rating
composite_ratings = Hash.new(0)
merged_ratings.each do |rating|
composite_ratings[rating[0]]+=rating[1]
end
composite_ratings.to_a
Check this fiddle: http://rubyfiddle.com/riddles/2d0f9/2

ActiveRecord query

I have the following ActiveRecords
class Product < ActiveRecord::Base
has_many :reviews
end
class Review < ActiveRecord::Base
belongs_to :product
end
Each review object contains a field named 'rating'
I wish to get a list of all products whose average rating is larger than a specific bound.
I don't understand how to use the find command to do this.
Does find let us do things like that?
I'd use SQL when queries start asking for things like conditions on aggregated values as this one does. There are various ways of achieving the result you want - this feels most straightforward to me:
bound = 3
products = Product.where('id in
(
select product_id
from reviews
group by product_id
having avg(rating) > ?)', bound)
-Yes, it can do it. Somthing like this should do the trick ...
Product.find(:all, :include => 'reviews', :conditions => ['review.rating > ?', min_rating])
Edit - Just reread your question. You want to use the average rating. I'd resort to SQL to do this, or if it is a common operation, calculate the average rating and store it in the product every time a rating is saved.

Resources