Reputation: 45922
Here is my Django code that does not work as expected:
posts = Post.objects.all().order_by('-added')[:20] # ordered by 'added'
post_list = dict([(obj.id, obj) for obj in posts])
# ... some operations with dictionary elements go here ...
posts_to_return = [post for post_id, post in post_list.items()] # order by 'id' now!
Is there a way to keep the original element order, so the posts would be ordered by added
in posts_to_return
?
Thank you!
EDIT: Python 2.6, Django 1.3
Upvotes: 3
Views: 2609
Reputation: 8689
It's also worth noting that you can use one of Python's many dictionary implementations that maintains the keys in sorted order. This is critical if you plan to do any insertions into your sorted dict. Consider the sortedcontainers module which is pure-Python and fast-as-C implementations. There's a SortedDict implementation that supports exactly what you need.
>>> from sortedcontainers import SortedDict
>>> posts = Post.objects.all().order_by('-added')[:20] # ordered by 'added'
>>> post_list = SortedDict([(obj.id, obj) for obj in posts])
>>> # ... some operations with dictionary elements go here ...
>>> # This is now automatically ordered by id:
>>> posts_to_return = [post for post_id, post in post_list.items()]
There's also a performance comparison that benchmarks several popular options against one another.
Upvotes: 4
Reputation: 91019
Use SortedDict instead of dict (from django.utils.datastructures import SortedDict
)
SortedDict maintains it's order in it's keyOrder
attribute. So you can manipulate the ordering without reconstructing dict if you want to. For example, to reverse the SortedDict's order just use keyOrder.reverse()
post_list = SortedDict([(obj.id, obj) for obj in posts])
# reversing the post order in-place
post_list.keyOrder.reverse()
Upvotes: 6
Reputation: 150997
Because no one has drawn attention to it yet, I'll simply note that the OrderedDict docs indicate that this recipe is equivalent and works on Python 2.4 and up. So at least you don't have to roll your own.
Upvotes: 1
Reputation: 123
You can use an OrderedDict instead of a Dict.
from collections import OrderedDict
...
post_list = OrderedDict([(obj.id, obj) for obj in posts])
Upvotes: 0
Reputation: 15702
You would need an ordered dictionary, which will be available in Python3.3 as far as I know. So you will probably have to order your result "by hand". Depending on your operations (which you havn't shown) it might be possible to just reuse the original posts list. But without knowing the operations, I can only guess.
Upvotes: 0
Reputation: 37441
Dicts in python (and most languages) have no order. You should instead use collections.OrderedDict
. This will retain the order of items as they are added. You can also look at the sorted()
builtin if the order of addition isn't the order you're trying to preserve.
Upvotes: 6