Join the Stack Overflow Community
Stack Overflow is a community of 6.6 million programmers, just like you, helping each other.
Join them; it only takes a minute:
Sign up

I'm trying to use an OrderedDict, but it keeps being created out of order. For example,

from collections import OrderedDict
OrderedDict(a=1,b=2,c=3)

yields

OrderedDict([('a', 1), ('c', 3), ('b', 2)])

rather than the expected

OrderedDict([('a', 1), ('b', 2), ('c', 3)])

How can I make sure it's created in the proper order I intend?

share|improve this question
up vote 21 down vote accepted

collections.OrderedDict keeps track of the order in which elements were added to it. This would work fine in a loop:

c = collections.OrderedDict()
for a,b in zip('abc', (1,2,3)):
    c[a] = b

However, the expression OrderedDict(a=1,b=2,c=3) creates an OrderedDict by passing several keyword arguments to its constructor. In Python 2.7, the order of keyword arguments is not guaranteed. If you want that, you'll have to move to Python 3.6, which implements PEP 468, Preserving the order of **kwargs in a function.

The **kwargs syntax in a function definition indicates that the interpreter should collect all keyword arguments that do not correspond to other named parameters. However, Python does not preserved the order in which those collected keyword arguments were passed to the function. In some contexts the order matters. This PEP dictates that the collected keyword arguments be exposed in the function body as an ordered mapping.

share|improve this answer
    
Huh. Didn't realize they actually decided to document that behavior and guarantee it. Good to know. – ShadowRanger 3 hours ago
    
this OrderedDict( zip("abc",(1,2,3)) ) also work – Copperfield 3 hours ago
1  
@ShadowRanger - There's some ambiguity about whether the base dict type is ordered as anything but a CPython implementation detail, but, yes, keyword arguments are officially part of the language as of 3.6. – TigerhawkT3 3 hours ago
    
@Copperfield - Yes, that sends a single argument to the constructor, which then iterates over that argument in a manner similar to the demonstrated loop. That argument is never sent to an arbitrarily-ordered container, and it's a sequence, so its order is guaranteed. – TigerhawkT3 3 hours ago

Read the docs:

The OrderedDict constructor and update() method both accept keyword arguments, but their order is lost because Python’s function call semantics pass-in keyword arguments using a regular unordered dictionary.

You must pass the input as a sequence of tuples (or an existing ordered dict type) to preserve ordering.

share|improve this answer

That is because the keyword arguments (variable = value, ) that you pass will be consolidated into a Python dictionary first. And Python dictionaries are unordered. kwds will be that dictionary as you can see in the Init signature.

Init signature: OrderedDict(self, *args, **kwds)

This is how the OrderedDict will be initialized internally when you pass the keyword arguments:

for key, value in kwds.items():
   self[key] = value

Since kwds is unordered, you will get an unordered OrderedDict.

You may create the ordered dict like so:

from collections import OrderedDict
from string import ascii_lowercase

d = OrderedDict()
for a,b in enumerate(ascii_lowercase[:3], 1):
    d[b] = a

Or:

n=3
d = OrderedDict(zip(ascii_lowercase[:n], range(1,n+1))) 
print d 

Output:

OrderedDict([('a', 1), ('b', 2), ('c', 3)])
share|improve this answer
    
Okay, but how does one create the OrderedDict desired then? – wogsland 3 hours ago
    
@wogsland should use ascii_lowercase? :) – MYGz 3 hours ago

It's weird that it hasn't been mentioned already but the representation of OrderedDict shows you how to create it so that the order is kept:

OrderedDict([('a', 1), ('b', 2), ('c', 3)])

it's not meant as an obstacle that the representation is like that - it's because that representation can be used to create an identical ordered OrderedDict.


Just for completeness (it has been mentioned already), the order is lost because OrderedDict(a=1, b=2, c=3) catches these arguments as **kwargs, which is a normal unordered-dict. At least until python 3.6 came along and made the promise that the order of the kwargs will be kept when you pass it in as you did:

What’s New In Python 3.6

PEP 468: Preserving Keyword Argument Order

**kwargs in a function signature is now guaranteed to be an insertion-order-preserving mapping.

share|improve this answer

You can create the desired mapping by using sorted():

dict = {"a":"some_value", "b":"other_value", "c":"foo"}
ordered = OrderedDict(sorted(dict.items(), key=lambda t: t[0]))

This sorts the items prior to passing them to the OrderedDict constructor.

The key= section sets the sorting, and t[0] sorts by the dictionary key.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.