0

I have an array of objects obtained from some queries and the all the object have the attributes: id, title and quantity. The question is : How can I sum the quantity by id and save each fields? I've tried this snippet

h=Hash.new(0)
a.each do |el|
  h[el.id] += el.quantity
end

and this goes, but I lose the title! How can I have the title inside the hash h?

thanks!

UPDATE to a solution:

I thought that the data for the a array could be many thousands of records, so I decided to anticipate the aggregation to sum the quantity. Thank's to a example of solution of , now each time I get the block of records from master table, I elaborate the data in this way

@agg = Hash.new(Array.new)
master.scope.each do |mst|
  recs = mst.select.........
  recs.each do |el|
      store = @agg[el.id].empty? ? 0 : @agg[el.id][1]
      @agg[el.id] = [el.title, el.quantity+store]
  end
end   

at the end, @agg holds all and only the aggregated data.

If someone can provide a more general or elegant solution come forward.

Thank's to all.

Giorgio

0

3 Answers 3

1

A hash element has 2 values in it, the key and the, well, value; hence h[key]=value.

For your goal you can just make the value be an array/hash, the first element of which is the title and the second element the quantity.

However what you are doing is not summing/accumulating anything. You are just building a hash out of the elements. Every time you iterate you create h[id] = h[quantity] for that specific id. You're just turning your el objects into hashes. If you want to accumulate the values do this.

sum = 0
h = Hash.new(Array.new) 
a.each do |el|
  sum += el.quantity
  h[el.id] = [el.title, sum]
end

If what you ultimately want is just to end up with your objects accumulating quantities in order of ids try:

prior = nil
a.each do |el|
  el.quantity += prior.quantity unless prior.nil?
  prior = el
end
Sign up to request clarification or add additional context in comments.

3 Comments

Thank's, but i think I may have been to brief on my description above.
I need to sum quantity for each id and keep title to the left for print purpose. In sql could be select id, max(title), sum(quantity) from table group by id. I would like to make this work on sql, but i haven't find a way to aggregate ActiveRecord::Relation objects
Thank's to your example i've made a solution to my problem, See my edited question
0

A hash is a collection of key, value pairs, so you can't store id, quantity, and title in a single hash element. What you want is a hash with id or title as the key and a nested hash with title or id and quantity as a value. Something like:

h = Hash.new { |hash, key| hash[key] = {} }
a.each do |el|
  if h[el.id][el.title].nil?
    h[el.id][el.title] = el.quantity
  else 
    h[el.id][el.title] += el.quantity
  end
end

4 Comments

ah, now I see what you meant
thank's, this is very elegant, but tried right now, in rails 3.2.13 console and get error: Undefined method + for nil class
of course! when there's no quantity to add to... new edit coming.
Thanks this works! I tried it now, and it's a bit along the way to obtain the value of quantity : h[key].values[0].
0

if the title is unique to id you can do this,

h=Hash.new
a.each do |el|
  h[el.id] ||= {:title => el.title, :quantity => 0}
  h[el.id][:quantity] += el.quantity
end

Note that this would keep the first value of title it encounters for a id.

It might be faster if you can do it within the db query. Sthing like

recs = mst.select("SUM(quantity) as total_quantity, id, title").group(:id)

This would return the records grouped by unique id and the quantity for each group is summed and is available as rec.total_quantity.

3 Comments

thank's, but tried right now it get error: Can't convert Symbol into integer
fixed the issue by removing the 0 passed to initiate the hash. It was making the default value to be zero for all keys. Not needed as we initiate quantity as 0 within the loop.
This solution is truly elegant. for your updated answer: my mst recordset contains only rows with differents id, so gruping by id don't change anithing. I need to repeat the cycle because recs get values from two model: from mst a get parameters to filter the second. For brevity I've omitted the complete query. many thanks

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.