Rails: correct way of turning activerecord relation to array?

Ruby on-RailsRubyActiverecord

Ruby on-Rails Problem Overview


I was trying to select objects uniq by one attribute, using @videos.uniq{|p| p.author}

  time = Time.new(2014, 12)
  start_time = time.beginning_of_month
  end_time = time.end_of_month

  videos = Video.where("created_at > ? AND created_at < ?", start_time, end_time).where("likes > ?", 15)
  selected_videos = videos.uniq{|p| p.author}
  puts videos.count, videos.class, selected_videos.count 
 #=> 23, Video::ActiveRecord_Relation, 23

  videos_first = videos.first(23)
  selected_videos = videos_first.uniq{|p| p.author}
  puts videos_first.count, videos_first.class, selected_videos.count
  #=> 23, array, 10

.uniq is not for ActiveRecord_Relation. And the problem is that the query returns a Video::ActiveRecord_Relation, but I need array.

Certainly, this could be achieved by using to_a, but is this elegant?

  1. What's the correct way of handling this ?
  2. Is it possible to use .uniq for activerecord:relation?

Ruby on-Rails Solutions


Solution 1 - Ruby on-Rails

If you need to access to the query result, just use #to_a on ActiveRecord::Relation instance.

At rails guides you can find on notable changes at Rails 4.0: "Model.all now returns an ActiveRecord::Relation, rather than an array of records. Use Relation#to_a if you really want an array. In some specific cases, this may cause breakage when upgrading." That is valid for other relation methods like :where.

selected_videos = videos.to_a.uniq{|p| p.author}

Solution 2 - Ruby on-Rails

.uniq does not make much sense when it is applied across the full active-record record.

Given that at least one or more of the three attributes - id, created_at, and updated_at - are different for every row, applying videos.uniq{|p| p.author} where videos is a ActiveRecord::Relation including all fields, will return all the rows in the ActiveRecord::Relation.

When the ActiveRecord::Relation object has a subset of values, uniq will be able to figure out the distinct values from them.

Eg: videos.select(:author).uniq.count will give 10 in your example.

The difference between ActiveRecord::Relation#uniq and Array#uniq is that the Array version accepts a block and uses the return value of a block for comparison. The ActiveRecord::Relation version of uniq simply ignores the block.

Solution 3 - Ruby on-Rails

If you need the records, you can use ActiveRecord::Relation#load

> Causes the records to be loaded from the database if they have not been loaded already. You can use this if for some reason you need to explicitly load some records before actually using them. The return value is the relation itself, not the records. >

https://api.rubyonrails.org/classes/ActiveRecord/Relation.html#method-i-load

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questioncqcn1991View Question on Stackoverflow
Solution 1 - Ruby on-RailsAlejandro BabioView Answer on Stackoverflow
Solution 2 - Ruby on-RailsPrakash MurthyView Answer on Stackoverflow
Solution 3 - Ruby on-RailsRorizView Answer on Stackoverflow