Skip to content

using uniq with a block on an ActiveRecord::Relation #20189

@burnt43

Description

@burnt43

I have an issue with the uniq method.

I have a database with a table as below.

MariaDB [hpbx_development]> select * from asterisk_commands;
+-------+-----------------+---------------------+---------------------+-----------+----------+-------------------+
| id    | command         | created_at          | updated_at          | completed | override | virtual_server_id |
+-------+-----------------+---------------------+---------------------+-----------+----------+-------------------+
| 87914 | dialplan reload | 2015-05-18 14:27:01 | 2015-05-18 14:27:01 |         0 |        0 |                 6 |
| 87916 | dialplan reload | 2015-05-18 15:05:52 | 2015-05-18 15:05:52 |         0 |        0 |                 6 |
+-------+-----------------+---------------------+---------------------+-----------+----------+-------------------+
2 rows in set (0.00 sec)

I had some code that worked as I intended in Rails 3.X, but since upgrading to Rails 4.X I found some odd behavior.

I have a line in my application below:
AsteriskCommand.all.uniq {|x| "#{x.command}:#{x.virtual_server_id}"}

Basically, I consider 2 AsteriskCommands the same if they have the same string for command and same virtual_server_id the other fields don't matter. However, now in Rails 4 it looks like calling .uniq on a Relation does a SQL query behind the scenes

AsteriskCommand.all.uniq {|x| "#{x.command}:#{x.virtual_server_id}"}.to_sql
returns

"SELECT DISTINCT `asterisk_commands`.* FROM `asterisk_commands`"

This query is meaningless because each asterisk_command has a unique id in the table and as such won't actually do anything. I was able to correct this problem by first converting the Relation to an Array. AsteriskCommand.all.to_a.uniq {|x| "#{x.command}:#{x.virtual_server_id}"}.

To summarize, because of the behind the scenes query the block I give uniq is ignored. As you can see from below the difference between using to_a and not using to_a. I think that maybe uniq should behave differently if you give it a block.

irb(main):010:0> AsteriskCommand.all.uniq {|x| "#{x.command}:#{x.virtual_server_id}"}.length
=> 2
irb(main):011:0> AsteriskCommand.all.to_a.uniq {|x| "#{x.command}:#{x.virtual_server_id}"}.length
=> 1

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions