Reputation: 10796
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 collection using that list of ids? Without making another call to the database, if possible.
Thank you !!!
Upvotes: 10
Views: 4172
Reputation: 67900
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] }
Upvotes: 21
Reputation: 104
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
Upvotes: 0
Reputation: 160321
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.
Upvotes: 1
Reputation: 6675
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] }
Upvotes: 12