0

I'm struggling with a simple implementation on an array. I've read the various class methods but want to make sure I arrive at an elegant solution.

I basically want to know the raw count of how many of a certain tags there are in a given system. So if a tag belongs_to :person and a person can has_many :tags

I wonder how I might go about rendering the raw total of how many people have a "blue" tag (if there are 'blue','red', 'green' and 'orange' tags). This is the path I'm pursuing but I think the code will begin to smell soon..

<% @people.each do |person| %>
    <% if person.tags.include?('blue') %>
     <%= person.id %> # this is where I get stuck
    <% end %>
<% end %>

So, I'm displaying the object of all the people in the system that have a blue tag, but now I need to sum those people to just get a raw count. And I need to render this a couple times in the view, one for each color (hoping to just switch out the parameter in include).

This is starting to smell because it won't become very DRY. Should this be a class method? And should I convert the returned people back to an array and call .count ?

I can eventually get the raw number but I'm a little lost here about best practice here.

---UPDATE---

This is my updated solution so far based on Gene's feedback.

Now it is much cleaner, but now I am wondering about performance because it seems slow.

I have a helper method-

def tag_count_for_person(tags_name)
    @people.count {|person| person.tags.include?('tags_name') }
end 

And call this method for whatever tag I need to render in the view.

<div class="well span4">
<%= tag_count_for_person("green") %>
</div>
<div class="well span4">
<%= tag_count_for_person("blue") %>
</div>
<div class="well span4">
<%= tag_count_for_person("red") %>
</div>
<div class="well span4">
<%= tag_count_for_person("hungry") %>
</div>

It still smells I believe. I shouldn't need to call this method everytime I want to render a different tag in the view? Seems costly. But then I don't have the felxibility of adding differnent tags I want to render correct? I feel like I'm much closer.

1
  • @Gene is this why you mentioned if I 'only' need to search for blue? Commented Jun 4, 2013 at 23:17

2 Answers 2

1

but I think the code will begin to smell soon..

Well, it does smell already. This kind of logic doesn't belong in a view. You better put it into controller or model. Here's an example.

colors = ['blue', 'red', 'green']

@people = ...

@people_count = {} # here we'll store counts by color
@people.each do |person|
  colors.each do |col|
    if person.tags.include?(col)
      # increment count
      @people_count[col] ||= 0
      @people_count[col] += 1
    end
  end
end

An alternative approach, somewhat more elegant

colors = ['blue', 'red', 'green']
@people_count = colors.each_with_object({}) do |color, memo|
  memo[color] = @people.count{|p| p.tags.include?(color)}
end

Then, in the view

<%= @people_count['blue'] %>
Sign up to request clarification or add additional context in comments.

5 Comments

if colors is an accesible attribute on the person model then should it be a class method? (instead of in the controller i.e. fat model, skinny controller)
@Tmacram: is it a constant list, or it can vary from person to person?
it's a checkbox on the people/edit page and can vary person to person. Jim can be red and blue, Duke can be red, Reggie can be red, green, Billy can be blue. Then in the view it should render blue - 2, red - 3, green - 1
@Tmacram: that's the tags accessor, isn't it? As for the color list itself, seems that it should be a constant.
Your code makes sense to me thanks. But as far as the best practice, maybe I'm misunderstanding? The color is a type of tag..Maybe it would help to explain a tag could be anything, meaning I could tag Bill above as hungry as well. So I could then display all the hungry people in the system as well.
0

Well I agree with @Sergio Tulenntsev, but if you only need to count blue, you can do so in a way that doesn't smell too badly:

<%= @people.count {|person| person.tags.include?('blue') } %>

will produce the integer number of people who have the tag blue. It would be a bit cleaner to put this in a helper function.

1 Comment

but running this helper method for each tag I needed a count on wouldn't be a good idea performance-wise yes? As it would be running the count 4 separate times in the view if I needed to display in the view a count for blue, hungry, green, and sad.

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.