How do I sum a many to many value?
-
08-06-2021 - |
Question
Each User can have many Resources, and each of those Resources has many Votes, and each of those votes have a value attribute that I want to sum all that particular users resources.
If I were to type this in a syntactically incorrect way I want something like...
@user.resources.votes.sum(&:value)
, but that obviously won't work.
I believe I need to use collect
but I am not sure?
This is the closest I got but it prints them out, heh
<%= @user.resources.collect { |r| r.votes.sum(&:value) } %>
Solution
How can you tell who voted having a Vote instance? Your Vote model has to have voter_id
field and additional association:
# in Vote.rb
belongs_to :voter, class_name: 'User', foreign_key: 'voter_id'
And in your User model:
# in User.rb
has_may :submited_votes, class_name: 'Vote', foreign_key: 'voter_id'
So, @user.votes
(as David Underwood proposed) will give you @user resources' votes. And @user.submited_votes
will give you votes submitted by the @user.
Using just User <- Resource <- Vote relation won't allow you to separate some user's votes made by him and votes made for its resources.
OTHER TIPS
I'd recommend setting up a has_many :through
relationship between the User and Vote objects. Set the models up like this:
class User < ActiveRecord::Base
has_many :resources
has_many :votes, :through => :resources
end
class Resource < ActiveRecord::Base
belongs_to :user
has_many :votes
end
class Vote < ActiveRecord::Base
belongs_to :resource
end
Once this is done you can simply call user.votes
and do whatever you want with that collection.
For more info on has_many :through
relations, see this guide: http://guides.rubyonrails.org/association_basics.html#the-has_many-through-association
For a total sum this should work or something real close.
sum = 0
@user.resources.each do |r|
r.votes.each do |v|
sum += v.value
end
end
This might work for you:
@user.resources.map {|r| r.votes.sum(:value)}.sum
How many records do you have, there is a way to push this to the database level I believe, I would have to check, but if it is only a few records then doing this in ruby would probably be ok
Try this code
@user.resources.map(&:votes).flatten.map(&:value).sum