Rails .each show once for duplicate results

2019-04-02 18:50发布

问题:

Within a rails .each code, how can I eliminate duplicates from the result?

PMRelationships is a relationship table that links people and movies MGRelationships is a relationship table that links genres and movies

The process Im looking for is to find All a Person's genres through first PmRelationship then MgRelationship

<% @pm_relationships = PmRelationship.where(:people_id => @person.id) %>

<ul class="basic-info-genres">
  <% @pm_relationships.each do |pm_relationship| %>
    <% @movie=Movie.find(pm_relationship.movie_id) %>
    <% @mg_relationships = MgRelationship.where(:movie_id => @movie.id) %>
    <% @mg_relationships.each do |mg_relationship| %>
      <% @genre=Genre.find(mg_relationship.genre_id) %>
      <%= @genre.name %>
    <% end %>
  <% end %>
</ul>

For example, this code returns a list of values, as such:

Action Action Comedy Thriller Adventure Noir Action

How can I make it so that It removes the two Lorems and only show one

Basically, Get rid of any duplication to return a list as such

Action Comedy Thriller Adventure Noir

UPDATE

I've changed my simplified code to match that of my actual app

I am aware most of this needs to be in the controller but simplified sake I'm doing it in the views first

Side note to those who could mention that I needn't create _Relationship tables and could simply use models has_many_belongs_to etc.., I find the Relationship table method much easier to handle and control

Thanks and apologies for the confusion

回答1:

As you are aware that your can use associations, I highly recommend it. It will help you to clean up the code and will save database queries.

Another thing, you are missing <li></li> inside <ul></ul>.

Anyways, as per your current code, you can just populate your all @genre.name in a Set

# as you said in question, following code should be moved to controller
genre_names = Set.new
<% @pm_relationships = PmRelationship.where(:people_id => @person.id) %>

<% @pm_relationships.each do |pm_relationship| %>
  <% @movie=Movie.find(pm_relationship.movie_id) %>
  <% @mg_relationships = MgRelationship.where(:movie_id => @movie.id) %>

  <% @mg_relationships.each do |mg_relationship| %>
    <% @genre=Genre.find(mg_relationship.genre_id) %>
    <% genre_names.add(@genre.name) %>    
  <% end %>
<% end%>

# actual view code
<ul class="basic-info-genres">
  <%= "<li>#{genre_names.to_a.join('</li><li>')}</li>".html_safe %>
</ul>

May be you want to read more about Set

NOTE: After moving code to corresponding files, use appropriate variable types in controller and view, as required



回答2:

Try to replace your original code:

<% @mg_relationships.each do |mg_relationship| %>
<% @genre=Genre.find(mg_relationship.genre_id) %>
  <%= @genre.name %>
<% end %>

To:(this version did not broken your code structure, just change the position of the output code)

<% genre_names = []%>
<% @mg_relationships.each do |mg_relationship| %>
<% @genre=Genre.find(mg_relationship.genre_id) %>
  <%#= @genre.name %>
  <% genre_names |= [@genre.name]%>
<% end %>

<%= genre_names %> or <%= genre_names.join(" ") %>


回答3:

Using the uniq method on Array, you'll be able to remove duplicates.

See here for reference: http://www.ruby-doc.org/core-2.1.2/Array.html#method-i-uniq



回答4:

<% @worlds.collect(&:latin).uniq.each do |latin| %>
  <%= latin.name %>
<% end %>

This assumes there's a belongs_to association between World and Latin (I assumed this because of the World#latin_id field and the fact there's a model called Latin).

EDIT: if you have multiple Latin objects with the same name then collect the names then do uniq:

<% @worlds.collect(&:latin).collect(&:name).uniq.each do |name| %>
  <%= name %>
<% end %>


回答5:

you can try this to get uniq latin name

 <% @worlds.collect(&:latin).collect(&:name).uniq.each do |latin_name| %>
   <%= latin_name %>
<% end %>