2012-04-12 73 views
10

Tôi có mảng Python sau đây của các từ điển:Python: sắp xếp một loạt các từ điển với bộ so sánh tùy chỉnh?

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 } ] 

Tôi muốn sắp xếp nó bằng các giá trị thứ hạng, đặt hàng như sau: 1-2-3-4-0-0-0.

Nếu tôi cố gắng:

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

sau đó danh sách được sắp xếp theo thứ tự 0-0-0-1-2-3-4.

Làm cách nào để xác định hàm so sánh tùy chỉnh để đẩy số 0 vào cuối danh sách? Tôi tự hỏi nếu tôi có thể sử dụng một cái gì đó như methodcaller.

Trả lời

23

Lựa chọn 1:

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

Phương án 2:

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

Demo:

"Tôi muốn muốn sắp xếp nó theo các giá trị xếp hạng, thứ tự như sau: 1-2-3-4-0-0-0. " --original tấm áp phích

>>> 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] 

 

ý kiến ​​bổ sung:

"Xin bạn có thể giải thích cho tôi (một người mới Python) những gì nó làm tôi có thể thấy rằng đó là một lambda, mà tôi biết là một hàm ẩn danh: bit trong ngoặc là gì?"- OP bình luận

Indexing/lát ký hiệu:

itemgetter('rank') là điều tương tự như lambda x: x['rank'] là điều tương tự như chức năng:

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

Các [...] được gọi là indexing/ký hiệu slice, xem Explain Python's slice notation - Cũng lưu ý rằng someArray[n] là ký pháp phổ biến trong nhiều ngôn ngữ lập trình để lập chỉ mục, nhưng có thể không hỗ trợ các lát của biểu mẫu [start:end] hoặc [start:end:step].

key= vs cmp= vs so sánh giàu:

Đối với những gì đang xảy ra, có hai cách phổ biến để xác định làm thế nào một thuật toán sắp xếp làm việc: một là với một hàm key, và thứ hai là với một cmp chức năng (hiện không được dùng trong python, nhưng linh hoạt hơn rất nhiều). Trong khi chức năng cmp cho phép bạn tự ý chỉ định cách so sánh hai yếu tố (đầu vào: a, b; đầu ra: a<b hoặc a>b hoặc a==b). Mặc dù hợp pháp, nó cung cấp cho chúng tôi không có lợi ích lớn (chúng tôi sẽ phải lặp lại mã một cách vụng về), và một chức năng quan trọng là tự nhiên hơn cho trường hợp của bạn. (Xem "đối tượng so sánh giàu" để biết cách mặc nhiên xác định cmp= một cách thanh lịch nhưng có thể-quá mức.)

Thực hiện chức năng chủ chốt của bạn:

Đáng tiếc là 0 là một yếu tố của số nguyên và do đó có một thứ tự tự nhiên: 0 là bình thường < 1,2,3 ... Vì vậy, nếu chúng ta muốn áp đặt một quy tắc bổ sung, chúng ta cần phải sắp xếp danh sách ở mức "cao hơn". Chúng tôi thực hiện điều này bằng cách tạo khóa cho một bộ dữ liệu: các bộ dữ liệu được sắp xếp đầu tiên bởi phần tử thứ nhất của chúng, sau đó là phần tử thứ 2 của chúng. Đúng sẽ luôn luôn được đặt hàng sau khi sai, vì vậy tất cả các Trues sẽ được đặt hàng sau khi các Falses; sau đó chúng sẽ sắp xếp như bình thường: (True,1)<(True,2)<(True,3)<..., (False,1)<(False,2)<..., (False,*)<(True,*). Phương án thay thế (tùy chọn 2), chỉ gán các từ điển hạng-0 một giá trị vô cùng, vì nó được đảm bảo ở trên bất kỳ thứ hạng có thể nào.

More chung thay thế - đối tượng so sánh giàu:

Giải pháp thậm chí tổng quát hơn sẽ được tạo ra một lớp đại diện hồ sơ, sau đó thực hiện __lt__, __gt__, __eq__, __ne__, __gt__, __ge__, và tất cả các khác rich comparison operators, hoặc cách khác chỉ cần thực hiện một trong số đó và __eq__ và sử dụng @functools.total_ordering decorator. Điều này sẽ khiến đối tượng của lớp đó sử dụng logic tùy chỉnh bất cứ khi nào bạn sử dụng toán tử so sánh (ví dụ: x=Record(name='Joe', rank=12)y=Record(...)x<y); vì chức năng sorted(...) sử dụng < và các toán tử so sánh khác theo mặc định trong một loại so sánh, điều này sẽ làm cho hành vi tự động khi sắp xếp và trong các trường hợp khác bạn sử dụng < và các toán tử so sánh khác. Điều này có thể hoặc có thể không quá nhiều tùy thuộc vào trường hợp sử dụng của bạn.

Cleaner thay thế - không quá tải 0 với ngữ nghĩa:

Tôi tuy nhiên nên chỉ ra rằng đó là một chút nhân tạo để đưa 0s đằng sau 1,2,3,4, vv. Cho dù điều này là hợp lý phụ thuộc vào việc xếp hạng = 0 thực sự có nghĩa là xếp hạng = 0; nếu xếp hạng = 0 thực sự là "thấp hơn" so với xếp hạng = 1 (mà lần lượt thực sự "thấp hơn" so với xếp hạng = 2 ...). Nếu đây thực sự là trường hợp, thì phương pháp của bạn hoàn toàn ổn. Nếu đây không phải là trường hợp, thì bạn có thể xem xét bỏ qua mục nhập 'rank':... thay vì thiết lập 'rank':0. Sau đó, bạn có thể sắp xếp theo câu trả lời Lev Levitsky bằng cách sử dụng 'rank' in d, hoặc bằng cách:

Lựa chọn 1 với chương trình khác nhau:

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

Lựa chọn 2 với chương trình khác nhau:

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

sidenote: Dựa vào sự tồn tại của vô hạn trong python gần như là một đường biên hack, làm cho bất kỳ giải pháp nào được đề cập (tuple, đối tượng so sánh), của Lev filter-then-concatenate solution, và thậm chí có thể hơi phức tạp hơn cmp solution (được đánh máy bởi wilson), tổng quát hơn với các ngôn ngữ khác.

+0

Vui lòng giải thích câu trả lời không chính xác của bạn. =) – ninjagecko

+0

Tùy chọn 1 hoạt động! Cảm ơn. – Richard

+0

Bạn có thể giải thích cho tôi (một người mới sử dụng Python) những gì nó đang làm? Tôi có thể thấy rằng đó là một lambda, mà tôi biết là một chức năng vô danh: những gì các bit trong ngoặc? – Richard

-2

Chỉ cần chuyển cho "khóa" một chức năng tùy ý hoặc đối tượng có thể gọi - đó là những gì cần. itemgetter xảy ra là một chức năng như vậy - nhưng nó có thể hoạt động với bất kỳ chức năng nào bạn viết - nó chỉ cần lấy một tham số duy nhất làm đầu vào và trả về một đối tượng có thể trực tiếp có thể tính được để đạt được thứ tự bạn muốn.

Trong trường hợp này:

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

sorted_master_list = sorted(myarr, key=key_func) 

(nó cũng có thể được viết như một biểu thức lambda)

-3

Bạn có thể sử dụng chức năng trong param chính:

cho ass sắp xếp:

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

hoặc để xem:

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

Ngoài ra, bạn có thể đọc về chức năng được sắp xếp ở đây http://wiki.python.org/moin/HowTo/Sorting

+0

wow, tại sao -2 ??? – Rustem

+0

Vì bạn đã đề xuất một loại tăng dần hoặc giảm dần, và OP muốn một thứ hơi khác (thứ tự sắp xếp thông thường EXCEPT với các phần tử xếp hạng 0 được sắp xếp khác nhau.) Bạn cần đọc và hiểu câu hỏi ban đầu trước khi đăng câu trả lời chung. –

-1

Một cách hacky để làm điều đó là:

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

này hoạt động khá tốt nếu bạn biết cấp bậc tối đa của bạn.

+0

Điều này sẽ không hoạt động. 'itemgetter()' trả về một hàm *. Khi bạn đã sử dụng lambda, chỉ cần sử dụng 'x ['rank']' - bạn sẽ mất lợi thế về hiệu năng khi sử dụng 'itemgetter'. – ThiefMaster

+0

@ThiefMaster true – mensi

-3

thử sorted_master_list = sắp xếp (myarr, key = itemgetter (chữ 'rank'), đảo ngược = True)

+0

Điều này sẽ đưa ra một thứ tự của '4, 3, 2, 1, 0, 0, 0'. OP muốn '1, 2, 3, 4, 0, 0, 0'. –

1

Tôi muốn làm

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

chút Tôi đoán nó có thể được nén bằng cách nào đó.

1

Tôi đang nghiêng nhiều hơn về phía tạo ra một chức năng so sánh để xử lý các "0" cụ thể là:

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']) 

Tuy nhiên, có hình phạt hiệu suất trên tùy chỉnh chức năng so sánh.

+0

Đây là giải pháp 'cmp'. Tôi thích việc sử dụng kết hợp 'key =' và 'cmp =' như một khai thác tao nhã về cách các tham số 'sắp xếp' hoạt động trong python. Trong tiếng Anh, điều này nói "' so sánh các phần tử còn lại, phải theo thứ hạng: chúng bằng nhau nếu xếp hạng của chúng bằng nhau, nếu không thì lớn hơn nếu 0 hoặc phải lớn hơn nếu là 0, nếu không thì thực hiện so sánh mặc định'. Bi kịch hai dòng đầu tiên là cần thiết, nếu không các dòng giữa sẽ trả về các giá trị sai trước 'cmp' cuối cùng. Phương án thay thế sẽ loại bỏ hai dòng trên cùng và làm: 'if x == 0 và y! = 0' ...' elif y = 0 và x! = 0' ... 'else:'. – ninjagecko

+1

Cần lưu ý rằng 'cmp' biến mất trong Python 3. – senderle

-1

Bạn đang myarr ràng buộc ở đây không giống như mã Python hợp lệ (và không thực hiện trong phiên giao dịch viên của tôi

Rendering đó vào:.

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

Cung cấp cho tôi một cái gì đó mà tôi

Cách được đề xuất để thực hiện sắp xếp tùy chỉnh bằng Python là sử dụng mẫu DSU (trang trí, sắp xếp, không phân chia) Nếu bạn muốn sắp xếp từ điển theo giá trị thì trông giống như như:

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

...trong đó (v,k) for k,v in myarr.items() là biểu thức cho trang trí; sorted() là, rõ ràng là sắp xếp và bên ngoài x[1] for x in ... là bước cuối cùng undecorate.

Rõ ràng điều này có vẻ như một yêu cầu đủ phổ biến mà người ta có thể để quấn này trong một hàm:

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

Nếu bạn đã có một bộ sưu tập các trường hợp đối tượng mà bạn muốn sắp xếp theo thuộc tính một số bạn có thể sử dụng một cái gì đó như thế này:

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] 

Vì vậy, nếu chúng ta tạo ra một lớp đại diện cho tên/dữ liệu thứ hạng của bạn như thế này:

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) 

... và khởi tạo một danh sách những người sử dụng myarr:

name_rankings = [NameRanking (k, v) cho k, v trong myarr.items()]

... sau đó chúng tôi có thể nhận được bản sao được sắp xếp sử dụng:

names_rankings_by_rank = sort_by_attr('rank', name_rankings) 

(Có assert không phải là ý tưởng hay ở đây; đó là nơi bạn sẽ đưa vào xử lý ngoại lệ của riêng bạn hoặc ném mã phù hợp với ứng dụng của bạn).

Các vấn đề liên quan