Re-order Multiple Items by Number
We're working on some cool new image management tools for Doodlekit and one small feature we wanted to add was number re-ordering, like you can do in your Netflix queue.Maybe I'm dense but it took me quite a while to get the logic down for this and I went through a number of different revisions before I nailed it. I thought I'd post it here just incase anyone is looking for it, and for feedback
The algorithm I settled on is very simple. Get the elements that you want to change, remove all of them from the array, reinsert them at their new positions in the array (in ascending order), and reindex the entire array. This is all done before saving anything and you should only save what changed.
The example is from a Ruby on Rails controller, but it's simple enough that sans some Ruby magic it should easily translate to other languages.
Comments
| 2. | Joshua Morrison | 12/14/2010 17:16:34 |
Awesome, thanks! I've been searching for a Ruby on Rails 3.0.3 netflix-type queue logic and so far I think you are the only person that's sharing it on the web. Unfortunately I'm not familiar enough with Rails yet to know where to put this... I'm thinking under the "def index" in my particular resource's controller.
Two quick questions please:
1. Am I right in thinking that "order" and "idx" are just private variables where image.position is where you actually store the order number as an attribute/column in the table?
2. Is ".compact" another attribute/column you created for your image resource... or is it a method that calls something needed to complete the re-indexing?
Thanks for posting this, I love Ruby and Rails but I know I'm only at the tip of the iceberg in beginning to learn them. :)
Joshua
| 3. | Ben Kittrell | 12/15/2010 00:37:44 |
@Joshua
I'm glad you find it helpful. I went through quite a few different algorithms before I settled on this and figured someone else could use it.
1. That is correct. order is the value of each text box submitted. idx is just incrementing integer and is there so that the positions start at 1 and have no gaps.
2. .compact is a Array method that removes empty Array elements. If someone enters a position that is greater than the size of the Array then there will be empty elements.
| 4. | Joshua | 12/15/2010 23:48:12 |
Thanks Ben, I am so close to getting it to work. :)
I think my hangup is here:
order = params["order_#{image.image_id}".to_sym]
I'm confused at how you are grabbing the new-found order. ".to_sym" is easy enough to understand and I know you're grabbing a value using params to store in "order"... it's the middle that I'm not understanding.
I'm trying:
@projects = Project.all
changed = Array.new
# Get the projects that changed order
for project in @projects
order = params["order_#{@projects.project_id}".to_sym]
if !order.blank? && order =~ /^\d+$/ && project.priority.to_i != order.to_i
project.priority = order.to_i
changed << project
end
end
and getting:
undefined method `project_id' for #<Project:0x103102df0>
Thanks again for the code, been a great learning exercise.
| 5. | Ben Kittrell | 12/17/2010 10:46:02 |
So first you need to make sure that when you're generating the form you're putting the id's in the text box names. This is the only way to know what order you want to assign to what object.
Your's should look something like this.
<%= text_field_tag "order_#{project.id}", project.position, :length => 3, :class => 'position' %>
But the reason your's is failing is because by default Active Record Objects' primary key is just 'id'. So it should be project.id instead of project.project_id.


Post a Comment