我的问题是对another question的扩展,其中OP有一个字典,下面的字典,并希望根据子字典键对主键进行排序
Carey was 5.
Chan was 3.
Jose was 6.
LeAnne was 2.
Mark was 1.
Masur was 4.
2LeAnne3Chan4Masur5Carey1Mark6Jose
建议(下面引用)解决方案完美无缺。
myDict = {
'SER12346': {'serial_num': 'SER12346', 'site_location': 'North America'},
'ABC12345': {'serial_num': 'ABC12345', 'site_location': 'South America'},
'SER12345': {'serial_num': 'SER12345', 'site_location': 'North America'},
'SER12347': {'serial_num': 'SER12347', 'site_location': 'South America'},
'ABC12346': {'serial_num': 'ABC12346', 'site_location': 'Europe'}
}
但是,此解决方案按升序排序(按降序排序很简单)。我想知道是否可以定义排序顺序的混合,比如dicts = myDict.items()
dicts.sort(key=lambda (k,d): (d['site_location'], d['serial_num'],))
升序,serial_num
降序排列?
答案 0 :(得分:2)
这是一种解决方案,因为列表排序为stable。我稍微更改了数据以证明它有效。
此方法也在Python documentation中规定。
myDict = {
'SER12346': {'serial_num': 'SER12346', 'site_location': 'North America'},
'ABC12346': {'serial_num': 'ABC12345', 'site_location': 'Europe'},
'ABC12345': {'serial_num': 'ABC12345', 'site_location': 'South America'},
'SER12345': {'serial_num': 'SER12345', 'site_location': 'North America'},
'SER12347': {'serial_num': 'SER12347', 'site_location': 'South America'}
}
result = sorted(myDict.items(), key=lambda x: x[1]['site_location'], reverse=True)
result = sorted(result, key=lambda x: x[1]['serial_num'])
# [('ABC12345', {'serial_num': 'ABC12345', 'site_location': 'South America'}),
# ('ABC12346', {'serial_num': 'ABC12345', 'site_location': 'Europe'}),
# ('SER12345', {'serial_num': 'SER12345', 'site_location': 'North America'}),
# ('SER12346', {'serial_num': 'SER12346', 'site_location': 'North America'}),
# ('SER12347', {'serial_num': 'SER12347', 'site_location': 'South America'})]
答案 1 :(得分:2)
如果您确实需要自定义排序顺序,那么您可以使用该排序逻辑编写一个自定义对象,该逻辑将用作下面实际对象的包装。
from functools import total_ordering
# total_ordering keeps you from having to write each of
# __gt__, __ge__, __lt__, __le__. It requires __eq__ and one of the
# other comparison functions to be defined and the rest are assumed
# in terms of each other. (__ge__ = __gt__ or __eq__, __gt__ = not __le__), etc.
@total_ordering
class CustomSorter(object):
def __init__(self, data):
self.data = data
# the properties here are solely to make the code a little more readable
# in the rich comparators below. You can ignore them if you like.
@property
def serial_number(self):
return self.data[1]["serial_number"]
@property
def site_location(self):
return self.data[1]["site_location"]
def __eq__(self, other):
if not isinstance(other, CustomSorter):
raise NotImplementedError("CustomSorters can only sort with themselves")
return self.data[1] == other.data[1]
def __lt__(self, other):
if not isinstance(other, CustomSorter):
raise NotImplementedError("CustomSorters can only sort with themselves")
if self.site_location == other.site_location:
return self.site_number < other.site_number
else:
return not (self.site_location < other.site_location)
然后使用传统的decorate-sort-undecorate步骤。
myDict = {
'SER12346': {'serial_num': 'SER12346', 'site_location': 'North America'},
'ABC12345': {'serial_num': 'ABC12345', 'site_location': 'South America'},
'SER12345': {'serial_num': 'SER12345', 'site_location': 'North America'},
'SER12347': {'serial_num': 'SER12347', 'site_location': 'South America'},
'ABC12346': {'serial_num': 'ABC12346', 'site_location': 'Europe'}
}
sorters = [CustomSorter(tup) for tup in myDict.items()]
sorters.sort()
result = [sorter.data for sorter in sorters]
如果你实现一个函数来为你排序,这可能是最好的。
def sort_on(sorter, unsorted):
"""sort_on expects @sorter@ to be a class with rich comparison operations
that is a decorative wrapper around some data to be sorted. Additionally,
@sorter.data@ should refer to the underlying data structure.
"""
decorated = [sorter(unsort) for unsort in unsorted]
decosorted = decorated.sort()
sorted = [decosort.data for decosort in decosorted]
return sorted
result = sort_on(CustomSorter, myDict.items())
答案 2 :(得分:2)
我认为你使用元组的单一方法是最Pythonic的,所以我坚持这一点。如果值都是数字,你可以很容易地否定任何元组值来获得该部分键的反向顺序,但这里的问题是你想要否定一个字符串,对吧?所以让我们解决这个问题:
def str_to_neg_ords(s):
return tuple(-ord(c) for c in s)
现在您可以将此功能用作键的一部分,以进行嵌套的词典排序:
sorted(myDict.values(),
key=lambda d: (str_to_neg_ords(d['site_location']), d['serial_num']))