399

I have a dictionary that I declared in a particular order and want to keep it in that order all the time. The keys/values can't really be kept in order based on their value, I just want it in the order that I declared it.

So if I have the dictionary:

d = {'ac': 33, 'gw': 20, 'ap': 102, 'za': 321, 'bs': 10}

It isn't in that order if I view it or iterate through it. Is there any way to make sure Python will keep the explicit order that I declared the keys/values in?

wjandrea
  • 28,235
  • 9
  • 60
  • 81
roflwaffle
  • 29,590
  • 21
  • 71
  • 94

13 Answers13

372

From Python 3.6 onwards, the standard dict type maintains insertion order by default.

Defining

d = {'ac':33, 'gw':20, 'ap':102, 'za':321, 'bs':10}

will result in a dictionary with the keys in the order listed in the source code.

This was achieved by using a simple array with integers for the sparse hash table, where those integers index into another array that stores the key-value pairs (plus the calculated hash). That latter array just happens to store the items in insertion order, and the whole combination actually uses less memory than the implementation used in Python 3.5 and before. See the original idea post by Raymond Hettinger for details.

In 3.6 this was still considered an implementation detail; see the What's New in Python 3.6 documentation:

The order-preserving aspect of this new implementation is considered an implementation detail and should not be relied upon (this may change in the future, but it is desired to have this new dict implementation in the language for a few releases before changing the language spec to mandate order-preserving semantics for all current and future Python implementations; this also helps preserve backwards-compatibility with older versions of the language where random iteration order is still in effect, e.g. Python 3.5).

Python 3.7 elevates this implementation detail to a language specification, so it is now mandatory that dict preserves order in all Python implementations compatible with that version or newer. See the pronouncement by the BDFL. As of Python 3.8, dictionaries also support iteration in reverse.

You may still want to use the collections.OrderedDict() class in certain cases, as it offers some additional functionality on top of the standard dict type. Such as as being reversible (this extends to the view objects), and supporting reordering (via the move_to_end() method).

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • 1
    is this *langage specification* feature available in the official documentation somewhere ? (was not able to find it) – Manuel Selva Jan 06 '21 at 14:07
  • 4
    @ManuelSelva the [3.7 what’s new documents mention it](https://docs.python.org/3/whatsnew/3.7.html) (linking to the email I quoted). The [dictionary view objects section](https://docs.python.org/3/library/stdtypes.html#dictionary-view-objects) further documents iteration order (under `iter(dictview)`: *Keys and values are iterated over in insertion order.* and *Dictionary order is guaranteed to be insertion order.*). – Martijn Pieters Jan 07 '21 at 22:19
  • 2
    @ManuelSelva the [standard type hierarchy section](https://docs.python.org/3/reference/datamodel.html#the-standard-type-hierarchy) of the data model docs also cover the subject (*Dictionaries preserve insertion order, meaning that keys will be produced in the same order they were added sequentially over the dictionary.*). – Martijn Pieters Jan 07 '21 at 22:25
  • 3
    @ManuelSelva the convention is: unless *explicitly* stated something is a CPython implementation detail, the description of a standard type feature in the docs makes that feature part of the language spec. – Martijn Pieters Jan 07 '21 at 22:30
181
from collections import OrderedDict
OrderedDict((word, True) for word in words)

contains

OrderedDict([('He', True), ('will', True), ('be', True), ('the', True), ('winner', True)])

If the values are True (or any other immutable object), you can also use:

OrderedDict.fromkeys(words, True)
eumiro
  • 207,213
  • 34
  • 299
  • 261
  • 3
    Worth noting, of course, that the 'immutable' part isn't a hard and fast rule that Python will enforce - its "only" a good idea. – lvc Jun 11 '12 at 14:37
  • 11
    be aware that solutions like: `OrderedDict(FUTURE=[], TODAY=[], PAST=[])` wont't work, when mentioned aproach: `OrderedDict([('FUTURE', []), ('TODAY', []), ('PAST', [])])` will keep order. – andilabs Jun 05 '14 at 13:47
  • 2
    @andi I got another problem,when using jsonify, the OrderedDict seems lost it's order when generate the json data.Anyway to solve this? –  Mar 31 '16 at 02:57
  • https://github.com/pallets/flask/issues/974 this can be used to solve the problem.. –  Mar 31 '16 at 03:01
  • 8
    Python3.7 now has dict ordered by default. https://mail.python.org/pipermail/python-dev/2017-December/151283.html – sertsedat Sep 04 '18 at 08:05
  • As this is the highest ranking answer on this thread I would suggest editing in the fact that since Python 3.7 dictionaries are guarenteed to keep the insertion order – Tomerikoo Jul 17 '19 at 18:13
  • The `fromKeys` method helped me make a fast "OrderedSet" to remove duplicates: `list(OrderedDict.fromkeys(list_with_dups, True).keys())` – Noumenon Apr 06 '21 at 13:54
176

Rather than explaining the theoretical part, I'll give a simple example.

>>> from collections import OrderedDict
>>> my_dictionary=OrderedDict()
>>> my_dictionary['foo']=3
>>> my_dictionary['aol']=1
>>> my_dictionary
OrderedDict([('foo', 3), ('aol', 1)])
>>> dict(my_dictionary)
{'foo': 3, 'aol': 1}
Mohit Dabas
  • 2,333
  • 1
  • 18
  • 12
  • 18
    Is there a way to mass assign OrderedDict like the Dict type? –  Mar 31 '16 at 02:50
  • 3
    `OrderedDict` indeed solves the problem, but... in this particular example you get exactly the same result using a standard dictionary – Tonechas Apr 25 '16 at 19:08
  • 2
    @Tonechas: I just tried the example with a standard dictionary, and got `{'aol': 1, 'foo': 3}` So I think it's a good illustrative example. – twasbrillig May 03 '16 at 20:13
  • 4
    There's a lesson for everyone: it was discovered (I think around the 2.4 release) that Python's [predictable hashing might give rise to security vulnerabilities](http://bugs.python.org/issue13703), so now there's no guarantee that even two different runs of the same code will give the same ordering in a standard dict. – holdenweb Sep 01 '16 at 08:19
  • Why can't we pass some format of the order we want to mass initialize with our values? Instead of assigning in each line one value? (python2.7) – JavaSa Jun 06 '17 at 11:43
  • 1
    @tyan you can call `OrderedDict.update()` with an iterable containing key-value pairs: `d1.upate([(key1, val1), (key2, val2)])`. – Ruud Althuizen Sep 12 '18 at 11:06
42

Note that this answer applies to python versions prior to python3.7. CPython 3.6 maintains insertion order under most circumstances as an implementation detail. Starting from Python3.7 onward, it has been declared that implementations MUST maintain insertion order to be compliant.


python dictionaries are unordered. If you want an ordered dictionary, try collections.OrderedDict.

Note that OrderedDict was introduced into the standard library in python 2.7. If you have an older version of python, you can find recipes for ordered dictionaries on ActiveState.

mgilson
  • 300,191
  • 65
  • 633
  • 696
  • 1
    see @martijn's post above. From python 3.6 onwards, dict supports insertion ordering. – mrtpk Jul 17 '17 at 10:28
12

Dictionaries will use an order that makes searching efficient, and you cant change that,

You could just use a list of objects (a 2 element tuple in a simple case, or even a class), and append items to the end. You can then use linear search to find items in it.

Alternatively you could create or use a different data structure created with the intention of maintaining order.

Fire Lancer
  • 29,364
  • 31
  • 116
  • 182
7

I came across this post while trying to figure out how to get OrderedDict to work. PyDev for Eclipse couldn't find OrderedDict at all, so I ended up deciding to make a tuple of my dictionary's key values as I would like them to be ordered. When I needed to output my list, I just iterated through the tuple's values and plugged the iterated 'key' from the tuple into the dictionary to retrieve my values in the order I needed them.

example:

test_dict = dict( val1 = "hi", val2 = "bye", val3 = "huh?", val4 = "what....")
test_tuple = ( 'val1', 'val2', 'val3', 'val4')
for key in test_tuple: print(test_dict[key])

It's a tad cumbersome, but I'm pressed for time and it's the workaround I came up with.

note: the list of lists approach that somebody else suggested does not really make sense to me, because lists are ordered and indexed (and are also a different structure than dictionaries).

smushface
  • 89
  • 1
  • 2
7

You can't really do what you want with a dictionary. You already have the dictionary d = {'ac':33, 'gw':20, 'ap':102, 'za':321, 'bs':10}created. I found there was no way to keep in order once it is already created. What I did was make a json file instead with the object:

{"ac":33,"gw":20,"ap":102,"za":321,"bs":10}

I used:

r = json.load(open('file.json'), object_pairs_hook=OrderedDict)

then used:

print json.dumps(r)

to verify.

nealous3
  • 712
  • 1
  • 11
  • 20
  • 1
    So why not start with an OrderedDict from a list? The JSON file doesn't really add anything here. – Martijn Pieters Jun 13 '17 at 06:21
  • Yes, list is more useful to keep order but the answer was in regards to the question about ordering dictionaries. Just letting people know about the limitations of using a dictionary and giving them a possible work around if they need to use a dictionary for some reason. – nealous3 Jun 14 '17 at 10:33
5
from collections import OrderedDict
list1 = ['k1', 'k2']
list2 = ['v1', 'v2']
new_ordered_dict = OrderedDict(zip(list1, list2))
print new_ordered_dict
# OrderedDict([('k1', 'v1'), ('k2', 'v2')])
sanyassh
  • 8,100
  • 13
  • 36
  • 70
user11502624
  • 51
  • 1
  • 1
2

Another alternative is to use Pandas dataframe as it guarantees the order and the index locations of the items in a dict-like structure.

Ville
  • 4,088
  • 2
  • 37
  • 38
2

I had a similar problem when developing a Django project. I couldn't use OrderedDict, because I was running an old version of python, so the solution was to use Django's SortedDict class:

https://code.djangoproject.com/wiki/SortedDict

e.g.,

from django.utils.datastructures import SortedDict
d2 = SortedDict()
d2['b'] = 1
d2['a'] = 2
d2['c'] = 3

Note: This answer is originally from 2011. If you have access to Python version 2.7 or higher, then you should have access to the now standard collections.OrderedDict, of which many examples have been provided by others in this thread.

Evan Byrne
  • 1,105
  • 1
  • 12
  • 17
1

if you would like to have a dictionary in a specific order, you can also create a list of lists, where the first item will be the key, and the second item will be the value and will look like this example

>>> list =[[1,2],[2,3]]
>>> for i in list:
...     print i[0]
...     print i[1]

1
2
2
3
eyquem
  • 26,771
  • 7
  • 38
  • 46
pelos
  • 1,744
  • 4
  • 24
  • 34
  • 7
    That is not a "dictionary" because you cannot lookup items by their key without searching through the entire collection (taking O(n) time). – BHSPitMonkey May 01 '14 at 00:09
  • 1
    Yes, it is not a dictionary, but, depending on the situation, it could provide a valid solution the problem of the original poster. – SunSparc Aug 25 '15 at 21:21
1

Generally, you can design a class that behaves like a dictionary, mainly be implementing the methods __contains__, __getitem__, __delitem__, __setitem__ and some more. That class can have any behaviour you like, for example prividing a sorted iterator over the keys ...

Rasmus Kaj
  • 4,224
  • 1
  • 20
  • 23
0

You can do the same thing which i did for dictionary.

Create a list and empty dictionary:

dictionary_items = {}
fields = [['Name', 'Himanshu Kanojiya'], ['email id', 'hima@gmail.com']]
l = fields[0][0]
m = fields[0][1]
n = fields[1][0]
q = fields[1][1]
dictionary_items[l] = m
dictionary_items[n] = q
print dictionary_items
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343