I have a collection of objects @users
, each having its id
attribute.
@users = [#<User id:1>, #<User id:2>]
I also have an ordered Array of ids
.
ids = [2,1]
¿Is there a magical way to sort the collec开发者_如何转开发tion using that list of ids? Without making another call to the database, if possible.
Thank you !!!
In fact you don't need to sort, build an intermediate indexed hash, it's O(n):
users_by_id = Hash[@users.map { |u| [u.id, u] }]
users_by_id.values_at(*ids)
If you still wanted to try a sort approach, the Schwartzian transform would be adequate:
@users.sort_by { |u| ids.index(u.id) }
However, using index
within a loop is a red flag: O(n^2) time. We can build an intermediate hash to go back to O(n*log n):
indexes = Hash[ids.each_with_index.to_a]
@users.sort_by { |u| indexes[u.id] }
Try this. First, build up a reverse mapping from id -> user.
ids_users = {}
@users.each {|user| ids_users[user.id] = user}
Then, use the ids order
ids.collect{ |id| ids_users[id] }
You certainly don't need to go to the DB since you already have the User
objects, although since the users are in an array, you'd probably want to create a temporary map of id => User
to get the end result.
If you can access the id of each user by calling user.id, you could sort the array like this:
@users.sort!{|a,b| a.id <=> b.id }
If you only have the ids in a separate array from the objects you could do the following: Zip the two arrays together, sort the resulting array on the ids, then collect the sorted users from the result.
users_ids = @users.zip(ids) # creates an array of smaller arrays each holding [user, id]
users_ids.sort!{|a,b| a[1] <=> b[1]} # sorts on the id in each sub-array
sorted_users = users_ids.collect{|item| item[0]} #grabs the users, leaving the ids behind
Take a glance at this: http://ariejan.net/2007/01/28/ruby-sort-an-array-of-objects-by-an-attribute
精彩评论