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 %>

2 Answers 2

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.

Sign up to request clarification or add additional context in comments.

3 Comments

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]]
also it seems that this is not working with PostgreSQL (HEROKU)
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 in SQL. Unfortunately, it turns out that #all has changed its meaning since earlier. I updated my answer accordingly.
1

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)

2 Comments

I still haven't got enough reputation to +1 you but it works so well!
however this is not working with PostgreSQL (HEROKU)

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.