Moving items in ActiveRecord_Relation result? - ruby-on-rails

I want to be able to return User.all with current_user as the first result, and the rest sorted alphabetically by user.name.
What's the "Rails" way to do this? I think it's to convert the ActiveRecord_Relation to an array and then use a combo of .insert and .delete_at to move the target User from its current position to the front. Would I want to create a helper method for that? Or is there a completely different approach?
Thanks!

Not the most "railsy" way, but this should work:
users = User.all.append(User.find(current_user.id))
users = (users & users).reverse!

In one query:
users = User.where("id != ?", current_user.id).all.insert(0, User.find(current_user.id))
However, please remember that it's almost always a bad idea to build your site around User.all queries... after 10,000+ users your app will grind to a halt. Wherever you are doing this query you should paginate the results.

Related

Rails custom model method in where query

In my rails app I have defined in the Kid model a calculation based on the fields from the Kids DB. the method is as follows:
def flip_date
self.dob.advance(months: 10)
end
I want to use this in my controller as I have a method where I am defining something as follows:
new_kids = Kid.where(discharge_date: nil).where('flip_date > ?', Date.current.advance(year: 1).beginning_of_year)
However I keep getting the following error:
SQLite3::SQLException: no such column: flip_date: SELECT "kids".* FROM "kids" WHERE "kids"."discharge_date" IS NULL AND (flip_date < '2017-01-01')
Any ideas on how can I make this work? All help is appreciated!
If you really want to use model methods take a look at http://apidock.com/rails/v4.0.2/ActiveRecord/QueryMethods/select
For your case:
new_kids = Kid.where(discharge_date: nil).select{|k| k.flip_date > Date.current.advance(year: 1).beginning_of_year}
But select method takes every object in memory before returning final result. Hence I will advise to use normal where clause and instead of flip_date take dob (which is a column in database) in consideration.
Like this
new_kids = Kid.where(discharge_date: nil).where('dob > ?', <date criteria>)
The select method (http://apidock.com/rails/v4.0.2/ActiveRecord/QueryMethods/select) works great if you are okay with the return being an Array.
I am still looking for a way to do this with an ActiveRecord_Relation return.
If others know how to do this, it would be much appreciated if you can share.
This example doesn't respond to your specific code, but to the extent it helps someone else with a similar question, here's a very simple example of how .select can be really handy:
#expired_memberships = User.select{|u| u.membership_expired_yesterday?}
In that example you've looped through all your Users and filtered them based on a custom method you defined on the User model (membership_expired_yesterday?). Now you can easily do stuff with that collection like this example in a mailer:
#expirations.each do |user|
MembershipExpirationMailer.with(user: user).first_reminder.deliver_now
end

Removing an item from an in-memory collection

I have a collection that contains a class like:
locations = Location.all
class Location < ActiveRecord::Base
end
The location class has a property: code
I wan to remove an item from the collection if code == "unused".
How many different ways can I do this in ruby?
I am currently doing this:
locations = Location.all.select { |l| l.code != "unused" }
This works great but just wondering what other ways I could do this just for learning purposes (if there big performance advantages in another way that would be good to know also).
Update
Please ignore the fact that I am loading my collection initially from the database, that wasn't the point. I want to learn how to remove things in-memory not simple where clauses :)
You can simply fetch records from your database what you need:
Rails 4 onwards:
locations = Location.where.not(code: "unused")
Before Rails 4:
locations = Location.where("code != ?", "unused")
If you have a collection and you want to reject some items from it, then you can try this:
locations.reject! {|location| location.code != "unused"}
You are doing this the wrong way. In your case, you are retrieving all records from DB and getting an array of records. Then you are looking for records you need in the array. Instead, you should get the records directly from DB:
Location.where("code != 'unused'")
# or in Rails 4 and latest
Location.where.not(code: "unused")
If you need to remove records from DB, you can do it like this:
Location.where.not(code: "unused").destroy_all
If you just want to know what is the best way to remove elements from an existing array, I think you are on the right track. Besides select there are reject, reject!, delete_if methods. You can learn more about them in the documentation http://ruby-doc.org/core-2.3.1/Array.html
There is a related post that might give more information: Ruby .reject! vs .delete_if

How do i not include latest record but all record from an object in rails?

For example if i want to pull all #posts using #posts.all but i dont want to include the very last or latest record from that?
here is what i am trying to do,
#posts = Post.all(Without the very very latest record that was created.)
Basically all record but not the very last record.
I think it is not worth it to try to generate a SQL query that excludes the very last element. Especially a subquery might be slower than just loading all records into an Array and than excluding the last:
#posts = Post.all[0..-2]
Your other example from the comments would look like this:
#contact_prices = #contact.retail_prices.all.order("created_at DESC").load[0..-2]
Another option (depending on the order of your relation) might be to use offset:
#contact_prices = #contact.retail_prices.order("created_at DESC").offset(1)
This is the most direct way I think of doing what you're trying to do:
Post.limit(Post.count - 1)
If you want your query to allow pagination or other LIMIT queries, you could try something like
Post.where("id < ?", Post.last.id)
Lots of answers that will do the trick, but throwing out an additional option:
#posts = Post.where("id != ?", Post.last.id)
One line AR:
Post.where.not(id: Post.last&.id)

remove specific record from array of records all in rails

I am implementing search functionality in rails. When i search for users, the logged in user who is searching also comes in search. I want to avoid it.
This is in my search_method in controller
#matchedUsers = InUser.where("first_name like ?", "%#{params[:searchfnameInput]}%")
And i have user id in session[:user_id]. I want to exclude the record having id==session[:user_id] from #matchedUsers?
Thanks and Regards
Add another where clause to your query:
#matchedUsers = InUser.where("id<>?", current_user.id).where("first_name like ?", "%#{params[:searchfnameInput]}%")
The "id<>?" says "exclude the user with this ID".

How do I add an `.order` invocation to a ActiveRecord collection that I retrieved?

I am new in this world of Rails. And I cannot get my head around this problem. How to get #microposts ordered by the date the micropost was created.
This is the line
#microposts = current_user.followeds.map(&:microposts).flatten
I only managed to order by date the 'followeds', but that is not what I am looking for. All the attempts I have made gave me errors, so I guess I am not aware of some syntax.
Any help is welcome!
Normally, you would add an order clause, as in:
Micropost.where(:written_by => current_user.followeds).order(:created_at)
The way you currently have this line structured doesn't permit that, however, since order is only available on ActiveRecord::Relations, and once you do map you no longer have a Relation available to chain on.
If that's the case, you'll want something like:
current_user.followeds.map(&:microposts).flatten.sort_by { |m| m.created_at }
I think you should try to approach this from another angle.
How about something like this:
#microposts = Micropost.where(author_id: current_user.followed_ids).order(:created_at).all
You might of course have to exchange author_id for whatever foreign key you have to identify what user a Micropost was written by.
If you want to reverse the posts (newest first), you just write order("created_at desc") instead.

Resources