Python通过多个键对字典列表进行sorting

我有一个列表:

b = [{u'TOT_PTS_Misc': u'Utley, Alex', u'Total_Points': 96.0}, {u'TOT_PTS_Misc': u'Russo, Brandon', u'Total_Points': 96.0}, {u'TOT_PTS_Misc': u'Chappell, Justin', u'Total_Points': 96.0}, {u'TOT_PTS_Misc': u'Foster, Toney', u'Total_Points': 80.0}, {u'TOT_PTS_Misc': u'Lawson, Roman', u'Total_Points': 80.0}, {u'TOT_PTS_Misc': u'Lempke, Sam', u'Total_Points': 80.0}, {u'TOT_PTS_Misc': u'Gnezda, Alex', u'Total_Points': 78.0}, {u'TOT_PTS_Misc': u'Kirks, Damien', u'Total_Points': 78.0}, {u'TOT_PTS_Misc': u'Worden, Tom', u'Total_Points': 78.0}, {u'TOT_PTS_Misc': u'Korecz, Mike', u'Total_Points': 78.0}, {u'TOT_PTS_Misc': u'Swartz, Brian', u'Total_Points': 66.0}, {u'TOT_PTS_Misc': u'Burgess, Randy', u'Total_Points': 66.0}, {u'TOT_PTS_Misc': u'Smugala, Ryan', u'Total_Points': 66.0}, {u'TOT_PTS_Misc': u'Harmon, Gary', u'Total_Points': 66.0}, {u'TOT_PTS_Misc': u'Blasinsky, Scott', u'Total_Points': 60.0}, {u'TOT_PTS_Misc': u'Carter III, Laymon', u'Total_Points': 60.0}, {u'TOT_PTS_Misc': u'Coleman, Johnathan', u'Total_Points': 60.0}, {u'TOT_PTS_Misc': u'Venditti, Nick', u'Total_Points': 60.0}, {u'TOT_PTS_Misc': u'Blackwell, Devon', u'Total_Points': 60.0}, {u'TOT_PTS_Misc': u'Kovach, Alex', u'Total_Points': 60.0}, {u'TOT_PTS_Misc': u'Bolden, Antonio', u'Total_Points': 60.0}, {u'TOT_PTS_Misc': u'Smith, Ryan', u'Total_Points': 60.0}] 

我需要使用Total_Points反转的多键sorting,然后不会被TOT_PTS_Misc反转。

这可以在命令提示符下完成,如下所示:

 a = sorted(b, key=lambda d: (-d['Total_Points'], d['TOT_PTS_Misc'])) 

但是我必须通过一个函数来运行这个函数,在这个函数中我传入了列表和sorting键。 例如, def multikeysort(dict_list, sortkeys):

如何使用lambda行来对列表进行sorting,对于传递给multikeysort函数的任意数量的键,并考虑到sortkeys可以具有任意数量的键,并且需要反向sorting的键将被识别与之前的“ – ”?

这个答案适用于字典中的任何一种列 – 否定列不一定是数字。

 def multikeysort(items, columns): from operator import itemgetter comparers = [((itemgetter(col[1:].strip()), -1) if col.startswith('-') else (itemgetter(col.strip()), 1)) for col in columns] def comparer(left, right): for fn, mult in comparers: result = cmp(fn(left), fn(right)) if result: return mult * result else: return 0 return sorted(items, cmp=comparer) 

你可以这样调用它:

 b = [{u'TOT_PTS_Misc': u'Utley, Alex', u'Total_Points': 96.0}, {u'TOT_PTS_Misc': u'Russo, Brandon', u'Total_Points': 96.0}, {u'TOT_PTS_Misc': u'Chappell, Justin', u'Total_Points': 96.0}, {u'TOT_PTS_Misc': u'Foster, Toney', u'Total_Points': 80.0}, {u'TOT_PTS_Misc': u'Lawson, Roman', u'Total_Points': 80.0}, {u'TOT_PTS_Misc': u'Lempke, Sam', u'Total_Points': 80.0}, {u'TOT_PTS_Misc': u'Gnezda, Alex', u'Total_Points': 78.0}, {u'TOT_PTS_Misc': u'Kirks, Damien', u'Total_Points': 78.0}, {u'TOT_PTS_Misc': u'Worden, Tom', u'Total_Points': 78.0}, {u'TOT_PTS_Misc': u'Korecz, Mike', u'Total_Points': 78.0}, {u'TOT_PTS_Misc': u'Swartz, Brian', u'Total_Points': 66.0}, {u'TOT_PTS_Misc': u'Burgess, Randy', u'Total_Points': 66.0}, {u'TOT_PTS_Misc': u'Smugala, Ryan', u'Total_Points': 66.0}, {u'TOT_PTS_Misc': u'Harmon, Gary', u'Total_Points': 66.0}, {u'TOT_PTS_Misc': u'Blasinsky, Scott', u'Total_Points': 60.0}, {u'TOT_PTS_Misc': u'Carter III, Laymon', u'Total_Points': 60.0}, {u'TOT_PTS_Misc': u'Coleman, Johnathan', u'Total_Points': 60.0}, {u'TOT_PTS_Misc': u'Venditti, Nick', u'Total_Points': 60.0}, {u'TOT_PTS_Misc': u'Blackwell, Devon', u'Total_Points': 60.0}, {u'TOT_PTS_Misc': u'Kovach, Alex', u'Total_Points': 60.0}, {u'TOT_PTS_Misc': u'Bolden, Antonio', u'Total_Points': 60.0}, {u'TOT_PTS_Misc': u'Smith, Ryan', u'Total_Points': 60.0}] a = multikeysort(b, ['-Total_Points', 'TOT_PTS_Misc']) for item in a: print item 

尝试任一栏否定。 你会看到sorting顺序相反。

下一步:改变它,所以它不使用额外的类….


2016年1月17日

从这个答案中获取灵感从一个可迭代匹配条件中获得第一个项目的最佳方法是什么? ,我缩短了代码:

 from operator import itemgetter as i def multikeysort(items, columns): comparers = [ ((i(col[1:].strip()), -1) if col.startswith('-') else (i(col.strip()), 1)) for col in columns ] def comparer(left, right): comparer_iter = ( cmp(fn(left), fn(right)) * mult for fn, mult in comparers ) return next((result for result in comparer_iter if result), 0) return sorted(items, cmp=comparer) 

如果你喜欢你的代码简洁。


之后2016-01-17

这与python3(它消除了cmp参数sort )的作品:

 from operator import itemgetter as i from functools import cmp_to_key def multikeysort(items, columns): comparers = [ ((i(col[1:].strip()), -1) if col.startswith('-') else (i(col.strip()), 1)) for col in columns ] def comparer(left, right): comparer_iter = ( cmp(fn(left), fn(right)) * mult for fn, mult in comparers ) return next((result for result in comparer_iter if result), 0) return sorted(items, key=cmp_to_key(comparer)) 

受这个答案的启发我应该如何做自定义sorting在Python 3中?

http://stygianvision.net/updates/python-sort-list-object-dictionary-multiple-key/在这方面有不同的技巧。; 如果您的要求比“全双向多键”更简单,请看一看。 很明显,我刚刚提到的接受的答案和博客文章以某种方式影响了彼此,但我不知道该订单。

如果这个链接死掉了,那么上面没有提到的例子就是一个非常快速的概要:

 mylist = sorted(mylist, key=itemgetter('name', 'age')) mylist = sorted(mylist, key=lambda k: (k['name'].lower(), k['age'])) mylist = sorted(mylist, key=lambda k: (k['name'].lower(), -k['age'])) 
 def sortkeypicker(keynames): negate = set() for i, k in enumerate(keynames): if k[:1] == '-': keynames[i] = k[1:] negate.add(k[1:]) def getit(adict): composite = [adict[k] for k in keynames] for i, (k, v) in enumerate(zip(keynames, composite)): if k in negate: composite[i] = -v return composite return getit a = sorted(b, key=sortkeypicker(['-Total_Points', 'TOT_PTS_Misc'])) 

我知道这是一个相当古老的问题,但是没有一个答案提到Python保证sorting例程(如list.sort()sorted()的稳定sorting顺序,这意味着比较相等的项目保留其原始顺序。

这意味着ORDER BY name ASC, age DESC (使用SQL表示法)等效于字典列表可以像这样完成:

 items.sort(key=operator.itemgetter('age'), reverse=True) items.sort(key=operator.itemgetter('name')) 

反转/反转适用于所有可订购的types,而不仅仅是可以通过在前面放一个减号来否定的数字。

而且由于(至less)CPython中使用的Timsortalgorithm,实际上这实际上是相当快的。

我使用以下命令对许多列进行二维数组sorting

 def k(a,b): def _k(item): return (item[a],item[b]) return _k 

这可以扩展到在任意数量的项目上工作。 我倾向于认为find一个更好的访问模式,你的可sorting的键比编写一个花式比较好。

 >>> data = [[0,1,2,3,4],[0,2,3,4,5],[1,0,2,3,4]] >>> sorted(data, key=k(0,1)) [[0, 1, 2, 3, 4], [0, 2, 3, 4, 5], [1, 0, 2, 3, 4]] >>> sorted(data, key=k(1,0)) [[1, 0, 2, 3, 4], [0, 1, 2, 3, 4], [0, 2, 3, 4, 5]] >>> sorted(a, key=k(2,0)) [[0, 1, 2, 3, 4], [1, 0, 2, 3, 4], [0, 2, 3, 4, 5]] 
 from operator import itemgetter from functools import partial def _neg_itemgetter(key, d): return -d[key] def key_getter(key_expr): keys = key_expr.split(",") getters = [] for k in keys: k = k.strip() if k.startswith("-"): getters.append(partial(_neg_itemgetter, k[1:])) else: getters.append(itemgetter(k)) def keyfunc(dct): return [kg(dct) for kg in getters] return keyfunc def multikeysort(dict_list, sortkeys): return sorted(dict_list, key = key_getter(sortkeys) 

示范:

 >>> multikeysort([{u'TOT_PTS_Misc': u'Utley, Alex', u'Total_Points': 60.0}, {u'TOT_PTS_Misc': u'Russo, Brandon', u'Total_Points': 96.0}, {u'TOT_PTS_Misc': u'Chappell, Justin', u'Total_Points': 96.0}], "-Total_Points,TOT_PTS_Misc") [{u'Total_Points': 96.0, u'TOT_PTS_Misc': u'Chappell, Justin'}, {u'Total_Points': 96.0, u'TOT_PTS_Misc': u'Russo, Brandon'}, {u'Total_Points': 60.0, u'TOT_PTS_Misc': u'Utley, Alex'}] 

parsing是有点脆弱的,但至less它允许键之间可变数量的空间。

既然你已经习惯了lambda,这里是一个不太详细的解决scheme。

 >>> def itemgetter(*names): return lambda mapping: tuple(-mapping[name[1:]] if name.startswith('-') else mapping[name] for name in names) >>> itemgetter('a', '-b')({'a': 1, 'b': 2}) (1, -2)