I have a dictionary of values read from two fields in a database: a string field and a numeric field. The string field is unique, so that is the key of the dictionary.
I can sort on the keys, but how can I sort based on the values?
Note: I have read Stack Overflow question here How do I sort a list of dictionaries by a value of the dictionary? and probably could change my code to have a list of dictionaries, but since I do not really need a list of dictionaries I wanted to know if there is a simpler solution to sort either in ascending or descending order.
回答 0
Python 3.6+
x ={1:2,3:4,4:3,2:1,0:0}{k: v for k, v in sorted(x.items(), key=lambda item: item[1])}{0:0,2:1,1:2,4:3,3:4}
x = {1: 2, 3: 4, 4: 3, 2: 1, 0: 0}
{k: v for k, v in sorted(x.items(), key=lambda item: item[1])}
{0: 0, 2: 1, 1: 2, 4: 3, 3: 4}
Older Python
It is not possible to sort a dictionary, only to get a representation of a dictionary that is sorted. Dictionaries are inherently orderless, but other types, such as lists and tuples, are not. So you need an ordered data type to represent sorted values, which will be a list—probably a list of tuples.
Well, it is actually possible to do a “sort by dictionary values”. Recently I had to do that in a Code Golf (Stack Overflow question Code golf: Word frequency chart). Abridged, the problem was of the kind: given a text, count how often each word is encountered and display a list of the top words, sorted by decreasing frequency.
If you construct a dictionary with the words as keys and the number of occurrences of each word as value, simplified here as:
from collections import defaultdict
d = defaultdict(int)
for w in text.split():
d[w] += 1
then you can get a list of the words, ordered by frequency of use with sorted(d, key=d.get) – the sort iterates over the dictionary keys, using the number of word occurrences as a sort key .
for w in sorted(d, key=d.get, reverse=True):
print(w, d[w])
I am writing this detailed explanation to illustrate what people often mean by “I can easily sort a dictionary by key, but how do I sort by value” – and I think the original post was trying to address such an issue. And the solution is to do sort of list of the keys, based on the values, as shown above.
>>> d ={"third":3,"first":1,"fourth":4,"second":2}>>>for k, v in d.items():...print"%s: %s"%(k, v)...
second:2
fourth:4
third:3
first:1>>> d{'second':2,'fourth':4,'third':3,'first':1}
Whilst I found the accepted answer useful, I was also surprised that it hasn’t been updated to reference OrderedDict from the standard library collections module as a viable, modern alternative – designed to solve exactly this type of problem.
It can often be very handy to use namedtuple. For example, you have a dictionary of ‘name’ as keys and ‘score’ as values and you want to sort on ‘score’:
import collections
Player = collections.namedtuple('Player', 'score name')
d = {'John':5, 'Alex':10, 'Richard': 7}
sorting with lowest score first:
worst = sorted(Player(v,k) for (k,v) in d.items())
sorting with highest score first:
best = sorted([Player(v,k) for (k,v) in d.items()], reverse=True)
Now you can get the name and score of, let’s say the second-best player (index=1) very Pythonically like this:
player = best[1]
player.name
'Richard'
player.score
7
As of Python 3.6 the built-in dict will be ordered
Good news, so the OP’s original use case of mapping pairs retrieved from a database with unique string ids as keys and numeric values as values into a built-in Python v3.6+ dict, should now respect the insert order.
If say the resulting two column table expressions from a database query like:
SELECT a_key, a_value FROM a_table ORDER BY a_value;
would be stored in two Python tuples, k_seq and v_seq (aligned by numerical index and with the same length of course), then:
Hopefully this will lead to a thin layer OrderedDict implementation as a first step. As @JimFasarakis-Hilliard indicated, some see use cases for the OrderedDict type also in the future. I think the Python community at large will carefully inspect, if this will stand the test of time, and what the next steps will be.
Time to rethink our coding habits to not miss the possibilities opened by stable ordering of:
Keyword arguments and
(intermediate) dict storage
The first because it eases dispatch in the implementation of functions and methods in some cases.
The second as it encourages to more easily use dicts as intermediate storage in processing pipelines.
Raymond Hettinger kindly provided documentation explaining “The Tech Behind Python 3.6 Dictionaries” – from his San Francisco Python Meetup Group presentation 2016-DEC-08.
And maybe quite some Stack Overflow high decorated question and answer pages will receive variants of this information and many high quality answers will require a per version update too.
Caveat Emptor (but also see below update 2017-12-15):
As @ajcr rightfully notes: “The order-preserving aspect of this new implementation is considered an implementation detail and should not be relied upon.” (from the whatsnew36) not nit picking, but the citation was cut a bit pessimistic ;-). It continues as ” (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).”
So as in some human languages (e.g. German), usage shapes the language, and the will now has been declared … in whatsnew36.
Make it so. “Dict keeps insertion order” is the ruling. Thanks!
So, the version 3.6 CPython side-effect of dict insertion ordering is now becoming part of the language spec (and not anymore only an implementation detail). That mail thread also surfaced some distinguishing design goals for collections.OrderedDict as reminded by Raymond Hettinger during discussion.
回答 9
我有同样的问题,我这样解决了:
WantedOutput= sorted(MyDict, key=lambda x :MyDict[x])
I had the same problem, and I solved it like this:
WantedOutput = sorted(MyDict, key=lambda x : MyDict[x])
(People who answer “It is not possible to sort a dict” did not read the question! In fact, “I can sort on the keys, but how can I sort based on the values?” clearly means that he wants a list of the keys sorted according to the value of their values.)
Please notice that the order is not well defined (keys with the same value will be in an arbitrary order in the output list).
>>> x ={1:2,3:4,4:3,2:1,0:0}>>>from collections importCounter>>>#To sort in reverse order>>>Counter(x).most_common()[(3,4),(4,3),(1,2),(2,1),(0,0)]>>>#To sort in ascending order>>>Counter(x).most_common()[::-1][(0,0),(2,1),(1,2),(4,3),(3,4)]>>>#To get a dictionary sorted by values>>>from collections importOrderedDict>>>OrderedDict(Counter(x).most_common()[::-1])OrderedDict([(0,0),(2,1),(1,2),(4,3),(3,4)])
from django.utils.datastructures importSortedDictdef sortedDictByKey(self,data):"""Sorted dictionary order by key"""
sortedDict =SortedDict()if data:if isinstance(data, dict):
sortedKey = sorted(data.keys())for k in sortedKey:
sortedDict[k]= data[k]return sortedDict
from django.utils.datastructures import SortedDict
def sortedDictByKey(self,data):
"""Sorted dictionary order by key"""
sortedDict = SortedDict()
if data:
if isinstance(data, dict):
sortedKey = sorted(data.keys())
for k in sortedKey:
sortedDict[k] = data[k]
return sortedDict
As pointed out by Dilettant, Python 3.6 will now keep the order! I thought I’d share a function I wrote that eases the sorting of an iterable (tuple, list, dict). In the latter case, you can sort either on keys or values, and it can take numeric comparison into account. Only for >= 3.6!
When you try using sorted on an iterable that holds e.g. strings as well as ints, sorted() will fail. Of course you can force string comparison with str(). However, in some cases you want to do actual numeric comparison where 12 is smaller than 20 (which is not the case in string comparison). So I came up with the following. When you want explicit numeric comparison you can use the flag num_as_num which will try to do explicit numeric sorting by trying to convert all values to floats. If that succeeds, it will do numeric sorting, otherwise it’ll resort to string comparison.
def gen(originalDict):for x, y in sorted(zip(originalDict.keys(), originalDict.values()), key=lambda z: z[1]):yield(x, y)#Yields as a tuple with (key, value). You can iterate with conditional clauses to get what you want. for bleh, meh in gen(myDict):if bleh =="foo":print(myDict[bleh])
Of course, remember, you need to use OrderedDict because regular Python dictionaries don’t keep the original order.
from collections import OrderedDict
a = OrderedDict(sorted(originalDict.items(), key=lambda x: x[1]))
If you do not have Python 2.7 or higher, the best you can do is iterate over the values in a generator function. (There is an OrderedDict for 2.4 and 2.6 here, but
a) I don’t know about how well it works
and
b) You have to download and install it of course. If you do not have administrative access, then I’m afraid the option’s out.)
def gen(originalDict):
for x, y in sorted(zip(originalDict.keys(), originalDict.values()), key=lambda z: z[1]):
yield (x, y)
#Yields as a tuple with (key, value). You can iterate with conditional clauses to get what you want.
for bleh, meh in gen(myDict):
if bleh == "foo":
print(myDict[bleh])
You can also print out every value
for bleh, meh in gen(myDict):
print(bleh, meh)
Please remember to remove the parentheses after print if not using Python 3.0 or above
from dicts.sorteddict importValueSortedDict
d ={1:2,3:4,4:3,2:1,0:0}
sorted_dict =ValueSortedDict(d)print sorted_dict.items()[(0,0),(2,1),(1,2),(4,3),(3,4)]
#Assume dictionary to be:
d ={'apple':500.1,'banana':1500.2,'orange':1.0,'pineapple':789.0}# create a temporary list
tmp =[]# iterate through the dictionary and append each tuple into the temporary list for key, value in d.items():
tmptuple =(value, key)
tmp.append(tmptuple)# sort the list in ascending order
tmp = sorted(tmp)print(tmp)
如果要按降序对列表进行排序,只需将原始排序行更改为:
tmp = sorted(tmp, reverse=True)
使用列表推导,一个衬里将是:
#Assuming the dictionary looks like
d ={'apple':500.1,'banana':1500.2,'orange':1.0,'pineapple':789.0}#One liner for sorting in ascending orderprint(sorted([(v, k)for k, v in d.items()]))#One liner for sorting in descending orderprint(sorted([(v, k)for k, v in d.items()], reverse=True))
You may use a temporary list to help you to sort the dictionary:
#Assume dictionary to be:
d = {'apple': 500.1, 'banana': 1500.2, 'orange': 1.0, 'pineapple': 789.0}
# create a temporary list
tmp = []
# iterate through the dictionary and append each tuple into the temporary list
for key, value in d.items():
tmptuple = (value, key)
tmp.append(tmptuple)
# sort the list in ascending order
tmp = sorted(tmp)
print (tmp)
If you want to sort the list in descending order, simply change the original sorting line to:
tmp = sorted(tmp, reverse=True)
Using list comprehension, the one liner would be:
#Assuming the dictionary looks like
d = {'apple': 500.1, 'banana': 1500.2, 'orange': 1.0, 'pineapple': 789.0}
#One liner for sorting in ascending order
print (sorted([(v, k) for k, v in d.items()]))
#One liner for sorting in descending order
print (sorted([(v, k) for k, v in d.items()], reverse=True))
Sample Output:
#Asending order
[(1.0, 'orange'), (500.1, 'apple'), (789.0, 'pineapple'), (1500.2, 'banana')]
#Descending order
[(1500.2, 'banana'), (789.0, 'pineapple'), (500.1, 'apple'), (1.0, 'orange')]
回答 26
遍历字典并按其值降序对其进行排序:
$ python --version
Python3.2.2
$ cat sort_dict_by_val_desc.py
dictionary = dict(siis =1, sana =2, joka =3, tuli =4, aina =5)for word in sorted(dictionary, key=dictionary.get, reverse=True):print(word, dictionary[word])
$ python sort_dict_by_val_desc.py
aina 5
tuli 4
joka 3
sana 2
siis 1
Iterate through a dict and sort it by its values in descending order:
$ python --version
Python 3.2.2
$ cat sort_dict_by_val_desc.py
dictionary = dict(siis = 1, sana = 2, joka = 3, tuli = 4, aina = 5)
for word in sorted(dictionary, key=dictionary.get, reverse=True):
print(word, dictionary[word])
$ python sort_dict_by_val_desc.py
aina 5
tuli 4
joka 3
sana 2
siis 1
If your values are integers, and you use Python 2.7 or newer, you can use instead of dict. The most_common method will give you all items, sorted by the value.
Because of requirements to retain backward compatability with older versions of Python I think the OrderedDict solution is very unwise. You want something that works with Python 2.7 and older versions.
But the collections solution mentioned in another answer is absolutely superb, because you retrain a connection between the key and value which in the case of dictionaries is extremely important.
I don’t agree with the number one choice presented in another answer, because it throws away the keys.
I used the solution mentioned above (code shown below) and retained access to both keys and values and in my case the ordering was on the values, but the importance was the ordering of the keys after ordering the values.
from collections import Counter
x = {'hello':1, 'python':5, 'world':3}
c=Counter(x)
print c.most_common()
>> [('python', 5), ('world', 3), ('hello', 1)]