0

In my app, every post has many messages, and messages belong to post as well as the user.

So now I am trying to get an array of messages with unique users by the code below. But it seems to me that the arrays is still the same and are not unique.

Here is the code:

<% post.messages.uniq{|x| x.user_id}.each do |m| %>
   ...
<% end %>
Holger Just
  • 52,918
  • 14
  • 115
  • 123
Alex Luk
  • 41
  • 9

2 Answers2

2

I assume that post is an ActiveRecord object and messages is a has_many relation on that class.

When calling post.messages, you don't immediately get an array back. Instead, Rails first creates an ActiveRecord::Relation object which contains the query you just built before it is executed. This allows you to chain finder methods (e.g. where, select, ...) and build a full query before it is sent to the database for execution.

Now, as you don't have an array, its methods can operate differently (and accept different arguments) than Array methods. The uniq method in this case is an alias to distinct which instructs the query to be created to be DISTINCT. As you are not yet dealing with an actual array of objects, filtering with a block won't work here yet.

In order to solve this, you have two options:

  • You can force ActiveRecord to run the query and return an array using the to_a method where you can then use the Array's uniq method with a block as you attempted above:

    post.messages.to_a.uniq { |x| x.user_id }
    
  • You can create an appropriate query to only return the data you actually need. This can be challenging in this case as the required SQL is probably rather complex and might not be supported by ActiveRecord's query generator (and would this be created by hand). See this question for details how you could create appropriate queries.

Community
  • 1
  • 1
Holger Just
  • 52,918
  • 14
  • 115
  • 123
  • I have tried to use your first method and uniq still has no effect at all. When i look at the log, it's what the query is like in MYSQL: Message Load (0.2ms) SELECT DISTINCT "messages".* FROM "messages" WHERE "messages"."post_id" = ? ORDER BY "messages"."created_at" DESC [["post_id", 1]] – Alex Luk Oct 16 '15 at 13:34
  • also it seems that this is not working with PostgreSQL (HEROKU) – Alex Luk Oct 16 '15 at 16:23
  • When calling `uniq` in an `ActiveRecord::Relation`, this is exactly what is supposed to happen: the addition of the `DISTINCT` keyword in the SQL query, which has [a very specific meaning](http://www.tutorialspoint.com/sql/sql-distinct-keyword.htm) in SQL. Unfortunately, it turns out that `#all` has changed its meaning since earlier. I updated my answer accordingly. – Holger Just Oct 18 '15 at 19:13
0

The deprecated .uniq_by method will work i suppose. The documentation suggest to use Array.uniq instead, but its not the same. (Array.uniq is ignoring the given block)

If you don't want to use .uniq_by, try this: post.messages.group(:user_id)

Szegedi Róbert
  • 306
  • 1
  • 5