0

I have been searching everywhere but I can't seem to find this problem anywhere. In Rails 5.0.0.beta3 I need to sort a my @record = user.records with an association and it's record. The sort goes something like this.

@record = @record.sort_by { |rec| 
If user.fav_record.find(rec.id)
  User.fav_record(rec.id).created_at
Else 
  rec.created_at
End

This is just an example of what I do. But everything sorts fine.

The problem: This returns an array and not an Active Record Class.

I've tried everything to get this to return an Active Record Class. I've pushed the sorted elements into an ID array and tried to extract it them in that order, I've tried mapping. Every result that I get turns my previous active record into an array or hash. Now I need it to go back into an active record. Does anyone know how to convert an array or hash of that active record back into an Active Record class?

devlin carnate
  • 8,309
  • 7
  • 48
  • 82

1 Answers1

2

There isn't a similarly easy way to convert ActiveRecord to array.

If you want to optimize the performance of your app, you should try to avoid converting arrays to ActiveRecord queries. Try and keep the object as a query as long as possible.

That being said, working with arrays is generally easier than queries, and it can feel like a hassle to convert a lot of array operations to ActiveRecord query (SQL) code.

It'd be better to write the sort code using ActiveRecord::Query methods or even writing it in plain SQL using find_by_sql.

I don't know what code you should specifically use here, but I do see that your code could be refactored to be clearer. First of all, If and Else should not be capitalized, but I'm assuming that this is just pseudocode and you already realize this. Second, your variable names should be pluralized if they are queries or arrays (i.e. @record.sort_by should be @records.sort_by instead).

It's worth mentioning that ActiveRecord queries are difficult to master and a lot of people just use array operations instead since they're easier to write. If "premature optimization is the root of all evil", it's really not the end of the world if you sacrifice a bit of performance and just keep your array implementation if you're just trying to make an initial prototype. Just make sure that you're not making "n+1" SQL calls, i.e. do not make a database call every iteration of your loop.

Here's an example of an array implementation which avoids the N+1 SQL issue:

# first load all the user's favorites into memory
user_fav_records = user.fav_records.select(:id, :created_at)
@records = @records.sort_by do |record|
  matching_rec = user.fav_records.find { |x| x.id.eql?(rec.id) }
  # This is using Array#find, not the ActiveRecord method
  if matching_rec
    matching_rec.created_at
  else 
    rec.created_at
  end
end

The main difference between this implementation and the code in your question is that I'm avoiding calling ActiveRecord's find each iteration of the loop. SQL read/writes are computationally expensive, and you want your code to make as little of them as possible.

max pleaner
  • 26,189
  • 9
  • 66
  • 118
  • Thank you so much for the explanation and code example . And yes, the code that I wrote is just pseudocode. I wrote the post on my Android device so the capitalizations and pluralized words were just typos. So that being said. I will try the example you shared and update the post if the problem was solved. Thanks again. – Felix Ramirez Apr 25 '16 at 07:14
  • Hmm, I tested the algorithm out. Although it is more efficient. I still have the same issue. @records is no longer an Active Record after the sort. It's just an array, thus causing my view to crash. So, I am going to attempt to write that exact pattern we use with the algorithm into an SQL statement. but thanks again for your input. Greatly appreciate it. – Felix Ramirez Apr 25 '16 at 09:06
  • 1
    @FelixRamirez one of the main concepts of MVC is to avoid putting database logic in your views. So I'd recommend refactoring your view to not depend on your objects being queries. By the way, there are ways to use `find` and preserve the ordering of the objects. See [here](http://stackoverflow.com/questions/1680627/activerecord-findarray-of-ids-preserving-order) for more info. Someone on that question posted [this gem](https://github.com/panorama-ed/order_as_specified) which seems useful. – max pleaner Apr 26 '16 at 16:20