Silver Light
Silver Light

Reputation: 45922

Python: Element order in dictionary

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

Answers (7)

GrantJ
GrantJ

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

Imran
Imran

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

arie
arie

Reputation: 18972

As you are using Django you could use SortedDict (docs)

Upvotes: 3

senderle
senderle

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

Pete Aykroyd
Pete Aykroyd

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

Achim
Achim

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

Daenyth
Daenyth

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

Related Questions