56

I'm sorting a Python 3.2.2 dictionary by its keys into a sorted list with 2 argument members, but can not make it a sorted dictionary in the end:

myDic={10: 'b', 3:'a', 5:'c'}
sorted_list=sorted(myDic.items(), key=lambda x: x[0])
1
  • 4
    What do you mean by "can not make a dictionary out of this sorted list"? A dictionary is unsorted by definition. If you want a sorted dictionary, the look at Ordered Dictionaries. Commented Jun 18, 2012 at 19:29

13 Answers 13

75

A modern and fast solution, for Python 3.7. May also work in some interpreters of Python 3.6.

TLDR

To sort a dictionary by keys use:

sorted_dict = {k: disordered[k] for k in sorted(disordered)}

Almost three times faster than the accepted answer; probably more when you include imports.

Comment on the accepted answer

The example in the accepted answer instead of iterating over the keys only - with key parameter of sorted() or the default behaviour of dict iteration - iterates over tuples (key, value), which suprisingly turns out to be much slower than comparing the keys only and accessing dictionary elements in a list comprehension.

How to sort by key in Python 3.7

The big change in Python 3.7 is that the dictionaries are now ordered by default.

  • You can generate sorted dict using dict comprehensions.
  • Using OrderedDict might still be preferable for the compatibility sake.
  • Do not use sorted(d.items()) without key.

See:

disordered = {10: 'b', 3: 'a', 5: 'c'}

# sort keys, then get values from original - fast
sorted_dict = {k: disordered[k] for k in sorted(disordered)}

# key = itemgetter - slower
from operator import itemgetter
key = itemgetter(0)
sorted_dict = {k: v for k, v in sorted(disordered.items(), key=key)}

# key = lambda - the slowest
key = lambda item: item[0]
sorted_dict = {k: v for k in sorted(disordered.items(), key=key)} 

Timing results:

Best for {k: d[k] for k in sorted(d)}: 7.507327548999456
Best for {k: v for k, v in sorted(d.items(), key=key_getter)}: 12.031082626002899
Best for {k: v for k, v in sorted(d.items(), key=key_lambda)}: 14.22885995300021

Best for dict(sorted(d.items(), key=key_getter)): 11.209122000000207
Best for dict(sorted(d.items(), key=key_lambda)): 13.289728325995384
Best for dict(sorted(d.items())): 14.231471302999125

Best for OrderedDict(sorted(d.items(), key=key_getter)): 16.609151654003654
Best for OrderedDict(sorted(d.items(), key=key_lambda)): 18.52622927199991
Best for OrderedDict(sorted(d.items())): 19.436101284998585

Testing code:

from timeit import repeat

setup_code = """
from operator import itemgetter
from collections import OrderedDict
import random
random.seed(0)
d = {i: chr(i) for i in [random.randint(0, 120) for repeat in range(120)]}
key_getter = itemgetter(0)
key_lambda = lambda item: item[0]
"""

cases = [
    # fast
    '{k: d[k] for k in sorted(d)}',
    '{k: v for k, v in sorted(d.items(), key=key_getter)}',
    '{k: v for k, v in sorted(d.items(), key=key_lambda)}',
    # slower
    'dict(sorted(d.items(), key=key_getter))',
    'dict(sorted(d.items(), key=key_lambda))',
    'dict(sorted(d.items()))',
    # the slowest 
    'OrderedDict(sorted(d.items(), key=key_getter))',
    'OrderedDict(sorted(d.items(), key=key_lambda))',
    'OrderedDict(sorted(d.items()))',
]

for code in cases:
    times = repeat(code, setup=setup_code, repeat=3)
    print(f"Best for {code}: {min(times)}")
Sign up to request clarification or add additional context in comments.

2 Comments

I'm surprised sorted(d.items()) is slower than the versions with a key. Sorting of tuples only compares the second and later items if all the earlier values are equal (which will never happen if the first values were dict keys). I wonder why that case is actually slow.
You are right. It might be due to cPython optimizations, e.g. github.com/python/cpython/commit/…
51

dict does not keep its elements' order. What you need is an OrderedDict: http://docs.python.org/library/collections.html#collections.OrderedDict

edit

Usage example:

>>> from collections import OrderedDict
>>> a = {'foo': 1, 'bar': 2}
>>> a
{'foo': 1, 'bar': 2}
>>> b = OrderedDict(sorted(a.items()))
>>> b
OrderedDict([('bar', 2), ('foo', 1)])
>>> b['foo']
1
>>> b['bar']
2

6 Comments

I cant manage to make such an OrderedDict object, I type OrderedDict={} but its just a regular variable named OrderedDict..
@Jjang, OrderedDict = {} just creates an ordinary dictionary named "OrderedDict." You need to do from collections import OrderedDict, then initialize with something like myOrdDic = OrderedDict().
but in the end it stays a list, not a dict... when you print OrderedDict it prints like a list, with (), not {} like a dict...
An ordered dictionary is not necessarily a sorted one unless the entries are added to it in the proper sequence. It will likely become unsorted again if an entry is added afterwards. To keep it sorted it would require it to essentially be recreated on each addition and using one would therefore not be the best way to implement such a thing.
This is recently out of date: since Python 3.7 dictionaries are ordered . See following answer (and this fun post softwaremaniacs.org/blog/2020/02/05/dicts-ordered/en)
|
18

I don't think you want an OrderedDict. It sounds like you'd prefer a SortedDict, that is a dict that maintains its keys in sorted order. The sortedcontainers module provides just such a data type. It's written in pure-Python, fast-as-C implementations, has 100% coverage and hours of stress.

Installation is easy with pip:

pip install sortedcontainers

Note that if you can't pip install then you can simply pull the source files from the open-source repository.

Then you're code is simply:

from sortedcontainers import SortedDict
myDic = SortedDict({10: 'b', 3:'a', 5:'c'})
sorted_list = list(myDic.keys())

The sortedcontainers module also maintains a performance comparison with other popular implementations.

1 Comment

For those using Anaconda, this is now available as a conda install using conda install sortedcontainers
11

Python's ordinary dicts cannot be made to provide the keys/elements in any specific order. For that, you could use the OrderedDict type from the collections module. Note that the OrderedDict type merely keeps a record of insertion order. You would have to sort the entries prior to initializing the dictionary if you want subsequent views/iterators to return the elements in order every time. For example:

>>> myDic={10: 'b', 3:'a', 5:'c'}
>>> sorted_list=sorted(myDic.items(), key=lambda x: x[0])
>>> myOrdDic = OrderedDict(sorted_list)
>>> myOrdDic.items()
[(3, 'a'), (5, 'c'), (10, 'b')]
>>> myOrdDic[7] = 'd'
>>> myOrdDic.items()
[(3, 'a'), (5, 'c'), (10, 'b'), (7, 'd')]

If you want to maintain proper ordering for newly added items, you really need to use a different data structure, e.g., a binary tree/heap. This approach of building a sorted list and using it to initialize a new OrderedDict() instance is just woefully inefficient unless your data is completely static.

Edit: So, if the object of sorting the data is merely to print it in order, in a format resembling a python dict object, something like the following should suffice:

def pprint_dict(d):
    strings = []
    for k in sorted(d.iterkeys()):
        strings.append("%d: '%s'" % (k, d[k]))
    return '{' + ', '.join(strings) + '}'

Note that this function is not flexible w/r/t the types of the key, value pairs (i.e., it expects the keys to be integers and the corresponding values to be strings). If you need more flexibility, use something like strings.append("%s: %s" % (repr(k), repr(d[k]))) instead.

9 Comments

+1 for "different data structure." An OrderedDict is more like a hybrid dict/queue; if you wouldn't use a queue to solve your problem, an OrderedDict probably isn't the right solution either.
@senderle, honestly, I have yet to encounter a single situation in any of my own work in which using an OrderedDict would be appropriate. That's not to say that it isn't conceivably a useful class, but I'd say it plays a very niche role.
but in the end it stays a list, not a dict... when you print OrderedDict it prints like a list, with (), not {} like a dict... And I need to print in a dict format in the end
@Jjang, it doesn't stay a list, it's merely that the .items() method happens to return a list. Internally, OrderedDict is not a list type.
@Jjang, you didn't specify the purpose of sorting the data. We can't read your mind. If all you want is to print the values in order, then just iterate over the sorted list of keys. I'll update my post with an example of what I mean in a minute.
|
6

With Python 3.7 I could do this:

>>> myDic={10: 'b', 3:'a', 5:'c'}
>>> sortDic = sorted(myDic.items())
>>> print(dict(sortDic))
{3:'a', 5:'c', 10: 'b'}

If you want a list of tuples:

>>> myDic={10: 'b', 3:'a', 5:'c'}
>>> sortDic = sorted(myDic.items())
>>> print(sortDic)
[(3, 'a'), (5, 'c'), (10, 'b')]

1 Comment

Great answer! Might be simpler to write that the answer is myDict = dict(sorted(myDict.items())) and then put the explanation afterward
2

Dictionaries are unordered by definition, What would be the main reason for ordering by key? A list of tuples created by the sort method can be used for whatever the need may have been, but changing the list of tuples back into a dictionary will return a random order

>>> myDic
{10: 'b', 3: 'a', 5: 'c'}
>>> sorted(myDic.items())
[(3, 'a'), (5, 'c'), (10, 'b')]
>>> print(dict(myDic.items()))
{10: 'b', 3: 'a', 5: 'c'}

Comments

1

Maybe not that good but I've figured this:

def order_dic(dic):
    ordered_dic={}
    key_ls=sorted(dic.keys())
    for key in key_ls:
        ordered_dic[key]=dic[key]
    return ordered_dic

1 Comment

Did you actually try your code? I doubt this works at all. Your ordered_dic is just as unsorted as dic. When you iterate a standard dict, the elements are not returned in the order they were inserted. You need OrderedDict for that.
1

Any modern solution to this problem? I worked around it with:

    order = sorted([ job['priority'] for job in self.joblist ])
    sorted_joblist = []
    while order:
        min_priority = min(order)
        for job in self.joblist:
            if job['priority'] == min_priority:
                sorted_joblist += [ job ]
                order.remove(min_priority)
    self.joblist = sorted_joblist

The joblist is formatted as: joblist = [ { 'priority' : 3, 'name' : 'foo', ... }, { 'priority' : 1, 'name' : 'bar', ... } ]

  • Basically I create a list (order) with all the elements by which I want to sort the dict
  • then I iterate this list and the dict, when I find the item on the dict I send it to a new dict and remove the item from 'order'.

Seems to be working, but I suppose there are better solutions.

Comments

1

I'm not sure whether this could help, but I had a similar problem and I managed to solve it, by defining an apposite function:

def sor_dic_key(diction):
    lista = []
    diction2 = {}
    for x in diction:
        lista.append([x, diction[x]])
    lista.sort(key=lambda x: x[0])
    for l in lista:
        diction2[l[0]] = l[1]
    return diction2

This function returns another dictionary with the same keys and relative values, but sorted by its keys. Similarly, I defined a function that could sort a dictionary by its values. I just needed to use x[1] instead of x[0] in the lambda function. I find this second function mostly useless, but one never can tell!

2 Comments

Hi Fabio, welcome to the site! This is a great first answer. I just edited it a little to get the code formatting right, but feel free to make more of your own changes if you want. Regarding your code, there are a few things you could simplify: Your first loop could be replaced by lista = list(diction.items()) (the items method returns a sequence of key-value tuples). Your second loop could be diction2 = dict(lista), since the dict constructor accepts sequences of key-value tuples and builds the dictionary from them.
Thank you very much for your answer, your help with formatting my comment and most of all for your suggestions. I started learning how to program with python three days ago and I was particularly proud of the solution I've found to this problem. Of course I know I'm just a novice, so once more thanks for your suggestions. I've just realized I could even use "lista = [(x, diction[x]) for x in diction]"
0

I like python numpy for this kind of stuff! eg:

r=readData()
nsorted = np.lexsort((r.calls, r.slow_requests, r.very_slow_requests, r.stalled_requests))

I have an example of importing CSV data into a numpy and ordering by column priorities. https://github.com/unixunion/toolbox/blob/master/python/csv-numpy.py

Kegan

Comments

0

The accepted answer definitely works, but somehow miss an important point.

The OP is asking for a dictionary sorted by it's keys this is just not really possible and not what OrderedDict is doing.

OrderedDict is maintaining the content of the dictionary in insertion order. First item inserted, second item inserted, etc.

>>> d = OrderedDict()
>>> d['foo'] = 1
>>> d['bar'] = 2
>>> d
OrderedDict([('foo', 1), ('bar', 2)])

>>> d = OrderedDict()
>>> d['bar'] = 2
>>> d['foo'] = 1
>>> d
OrderedDict([('bar', 2), ('foo', 1)])

Hencefore I won't really be able to sort the dictionary inplace, but merely to create a new dictionary where insertion order match key order. This is explicit in the accepted answer where the new dictionary is b.

This may be important if you are keeping access to dictionaries through containers. This is also important if you itend to change the dictionary later by adding or removing items: they won't be inserted in key order but at the end of dictionary.

>>> d = OrderedDict({'foo': 5, 'bar': 8})
>>> d
OrderedDict([('foo', 5), ('bar', 8)])
>>> d['alpha'] = 2
>>> d
OrderedDict([('foo', 5), ('bar', 8), ('alpha', 2)])

Now, what does mean having a dictionary sorted by it's keys ? That makes no difference when accessing elements by keys, this only matter when you are iterating over items. Making that a property of the dictionary itself seems like overkill. In many cases it's enough to sort keys() when iterating.

That means that it's equivalent to do:

>>> d = {'foo': 5, 'bar': 8}
>>> for k,v in d.iteritems(): print k, v

on an hypothetical sorted by key dictionary or:

>>> d = {'foo': 5, 'bar': 8}
>>> for k, v in iter((k, d[k]) for k in sorted(d.keys())): print k, v

Of course it is not hard to wrap that behavior in an object by overloading iterators and maintaining a sorted keys list. But it is likely overkill.

Comments

0

Sorting dictionaries by value using comprehensions. I think it's nice as 1 line and no need for functions or lambdas

a = {'b':'foo', 'c':'bar', 'e': 'baz'}
a = {f:a[f] for f in sorted(a, key=a.__getitem__)}

Comments

0

Easy and straightforward way:

op = {'1': (1,0,6),'3': (0,45,8),'2': (2,34,10)}                   
lp3 = sorted(op.items(), key=operator.itemgetter(0), reverse=True)
print(lp3)

ref: https://blog.csdn.net/weixin_37922873/article/details/81210032

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.