2012-04-12 86 views
10

我的字典以下Python的數組:Python:使用自定義比較器對字典數組進行排序?

myarr = [ { 'name': 'Richard', 'rank': 1 }, 
{ 'name': 'Reuben', 'rank': 4 }, 
{ 'name': 'Reece', 'rank': 0 }, 
{ 'name': 'Rohan', 'rank': 3 }, 
{ 'name': 'Ralph', 'rank': 2 }, 
{ 'name': 'Raphael', 'rank': 0 }, 
{ 'name': 'Robin', 'rank': 0 } ] 

我想通過等級值對它進行排序,排序如下:1-2-3-4-0-0-0。

如果我嘗試:

sorted_master_list = sorted(myarr, key=itemgetter('rank')) 

那麼列表的順序排序0-0-0-1-2-3-4。

如何定義一個自定義比較函數來將零推到列表的底部?我想知道我是否可以使用類似methodcaller的東西。

回答

23

選項1:

key=lambda d:(d['rank']==0, d['rank']) 

選項2:

key=lambda d:d['rank'] if d['rank']!=0 else float('inf') 

演示:

「我喜歡按照它排序等級值,排序如下:1-2-3-4-0-0-0。「 --original海報

>>> sorted([0,0,0,1,2,3,4], key=lambda x:(x==0, x)) 
[1, 2, 3, 4, 0, 0] 

>>> sorted([0,0,0,1,2,3,4], key=lambda x:x if x!=0 else float('inf')) 
[1, 2, 3, 4, 0, 0] 

 

附加註釋:?

「請你能不能給我(一個Python新手),它在做什麼解釋,我可以看到,它是一個lambda,我知道它是一個匿名函數:括號中的內容是什麼?「 - OP評論

索引/切片標誌

itemgetter('rank')是一回事lambda x: x['rank']的是同樣的事情功能:

def getRank(myDict): 
    return myDict['rank'] 

[...]被稱爲索引/切片符號,請參閱Explain Python's slice notation - 還請注意,someArray[n]是許多用於索引的編程語言的通用符號,但可能不支持格式爲的切片或[start:end:step]

key= VS cmp= VS豐富的比較

至於到底是怎麼回事,有指定的排序算法是如何工作的兩種常用方法:一種是用key功能,另一種是用cmp函數(現在在python中已棄用,但功能更多)。雖然cmp函數允許您任意指定兩個元素應該如何比較(輸入:a,b;輸出:a<ba>b或或a==b)。儘管是合法的,但它並沒有給我們帶來什麼好處(我們必須以笨拙的方式複製代碼),而且一個關鍵功能對於您的情況更自然。 (見「反對富人比較」如何在一個優雅的,但可能是,過度的方式來隱式地定義cmp=

實現你的關鍵功能

不幸的是0是整數的元素,因而具有自然排序:0通常是< 1,2,3 ...因此,如果我們想強加一個額外的規則,我們需要在「更高級別」排序列表。我們通過使關鍵字成爲元組來實現這一點:元組首先按第一個元素排序,然後按第二個元素排序。真假將永遠在假之後下令,所以所有的真主都會在假的後面下令;他們會按正常排序:(True,1)<(True,2)<(True,3)<...,(False,1)<(False,2)<...,(False,*)<(True,*)。替代方案(方案2)僅僅將0級字典賦予無窮大的值,因爲它保證高於任何可能的級別。

更一般替代 - 對象豐富的比較:

的更一般的解決方案是創建代表記錄的類,然後實現__lt____gt____eq____ne____gt____ge__,和所有其他rich comparison operators,或者只是實現其中的一個,__eq__並使用@functools.total_ordering decorator。這會導致該類的對象在您使用比較運算符時使用自定義邏輯(例如x=Record(name='Joe', rank=12)y=Record(...)x<y);因爲sorted(...)函數在比較排序中默認使用<和其他比較運算符,所以這會在排序時使行爲自動執行,並且在其他情況下使用<和其他比較運算符。這可能會或可能不會過多,具體取決於您的用例。

清潔的替代 - 不超載語義0:

我不過應該指出的是,這是一個有點人爲的把後面的0 1,2,3,4等。這是否合理取決於rank = 0是否意味着rank = 0;如果rank = 0真的比rank = 1「低」(反過來,它實際上是「低於」rank = 2 ......)。如果確實如此,那麼你的方法是完全正確的。如果情況並非如此,那麼您可以考慮省略'rank':...條目而不是設置'rank':0

選項1不同的方案:

key=lambda d: (not 'rank' in d, d['rank']) 

選項2不同的方案:

key=lambda d: d.get('rank', float('inf')) 

旁註:依託,那麼你可以通過使用'rank' in d,或者通過列弗Levitsky的答案排序在python中無窮大的存在幾乎是一個黑客的邊界,使任何提到的解決方案(元組,對象比較),列夫的filter-then-concatenate solution,甚至可能是稍微複雜的cmp solution (由威爾遜鍵入),更普遍的其他語言。

+0

請解釋您的不正確的downvote。 =) – ninjagecko 2012-04-12 18:35:45

+0

選項1的作品!謝謝。 – Richard 2012-04-12 18:37:08

+0

請你能向我解釋一下(Python新手)它在做什麼?我可以看到這是一個lambda,我知道它是一個匿名函數:括號內的數字是什麼? – Richard 2012-04-12 18:38:42

-2

只需給「關鍵」一個任意函數或可調用對象 - 它就是這樣。 itemgetter恰好就是這樣一個函數 - 但它可以用你編寫的任何函數工作 - 它只需要一個參數作爲輸入,並且返回一個對象,它可以直接compable來實現你想要的順序。

在這種情況下:

def key_func(item): 
    return item["rank"] if item["rank"] != 0 else -100000 

sorted_master_list = sorted(myarr, key=key_func) 

(它也可被寫爲一個lambda表達式)

-3

可以使用功能鍵PARAM:

用於屁股排序:

sorted_master_list = sorted(myarr, key=lambda x: x.get('rank')) 

或降序:

sorted_master_list = sorted(myarr, key=lambda x: -x.get('rank')) 

你也可以讀到這裏來分類的功能http://wiki.python.org/moin/HowTo/Sorting

+0

哇,爲什麼-2 ??? – Rustem 2012-04-12 18:39:12

+0

因爲您建議按照股票標準升序或降序排序,並且OP想要的東西略有不同(正常排序順序除了零排序元素排序不同外)。在發佈通用答案之前,您需要閱讀並理解原始問題。 – 2012-04-12 18:52:17

-1

哈克的方式來做到這一點是:

sorted_master_list = sorted(myarr, key=lambda x: 99999 if x['rank'] == 0 else x['rank']) 

這工作得相當好,如果你知道你的最大秩。

+0

這是行不通的。 'itemgetter()'返回一個*函數*。當你已經使用lambda時,只需使用'x ['rank']' - 無論如何你都會失去使用'itemgetter'的性能優勢。 – ThiefMaster 2012-04-12 18:35:21

+0

@ThiefMaster true – mensi 2012-04-12 18:37:44

-3

嘗試 sorted_master_list =排序(myArr,該,鍵= itemgetter( '級別'),反向= TRUE)

+0

這將給出'4,3,2,1,0,0,0'的排序。 OP要求'1,2,3,4,0,0,0'。 – 2012-04-12 18:54:50

1

我做

sortedlist = sorted([x for x in myarr if x['rank']], key=lambda x: x['rank']) + [x for x in myarr if not x['rank']] 

位我想這可能以某種方式壓縮。

1

我更傾向朝着建立一個比較函數來處理「0」的具體做法是:

def compare(x,y): 
    if x == y: 
     return 0 
    elif x == 0: 
     return 1 
    elif y == 0: 
     return -1 
    else: 
     return cmp(x,y) 

sorted(myarr, cmp=lambda x,y: compare(x,y), key=lambda x:x['rank']) 

不過,也有關於自定義比較函數的性能損失。

+0

這是'cmp'解決方案。我喜歡結合使用'key ='和'cmp ='作爲sort參數在Python中工作方式的優雅開發。用英語說,「比較左邊的元素和右邊的排名:如果它們的排名相等,則它們是相等的,否則如果它是0則左邊更大,如果它是0則右邊更大,否則執行默認比較」。不幸的是,前兩行是必要的,否則中間行會在最後一個'cmp'之前返回錯誤的值。替代方法是刪除前兩行,然後執行:'if x == 0 and y!= 0' ...'elif y = 0 and x!= 0' ...'else:'。 – ninjagecko 2012-04-12 19:26:47

+1

值得注意的是'cmp'在Python 3中消失了。 – senderle 2012-04-12 20:34:46

-1

myarr結合這裏看起來並不像有效的Python代碼(和我的翻譯會話不執行

渲染成:

myarr = { 
    'Richard': 1, 
    'Reuben': 4, 
    'Reece': 0, 
    'Rohan': 3, 
    'Ralph': 2, 
    'Raphael': 0, 
    'Robin': 0 } 

讓我上我的東西可以基於一個答案

在Python中進行自定義排序的推薦方法是使用DSU(裝飾,排序,undecorate)模式 如果你想通過值排序字典,那看起來像如:

keys_sorted_by_val = [ x[1] for x in sorted([(v,k) for k,v in myarr.items()])] 

...其中(v,k) for k,v in myarr.items()裝飾的表達; sorted()顯然是分類而外部x[1] for x in ...是最後的undecorate步驟。

顯然,這似乎是一個足夠的共同要求,即人們可能要在一個函數包裝這件事:

def dict_by_values(d): 
    return [ x[1] for x in sorted([(v,k) for k,v in d.items()])] 

如果你有,你要排序的某些屬性可以對象實例的集合使用這樣的事情:

def sort_by_attr(attr, coll): 
    results = list() 
    for each in coll: 
     assert hasattr(each, attr) 
     results.append((getattr(each, attr), each)) 
    results.sort() 
    return [x[1] for x in results] 

因此,如果我們創建了一個類代表這樣你的名字/排名數據:

class NameRanking(object): 
    def __init__(self, name, rank): 
     self.name = name 
     self.rank = rank 
    def __repr__(self): 
     return "%s: %s, %s" %(self.__class__, self.name, self.rank) 

...和使用myarr實例人的名單:

name_rankings = [NameRanking(K,V)對K,V在myarr.items()]

......那麼,我們可以得到在使用的排序副本:

names_rankings_by_rank = sort_by_attr('rank', name_rankings) 

(是的assert是不是在這裏是個好主意;那就是你將自己的異常處理或適當的代碼投入你的應用程序)。

相關問題