2010-11-20 5 views
232

私はリストのリストを持っている:リストを複数の属性でソートしますか?

[[12, 'tall', 'blue', 1], 
[2, 'short', 'red', 9], 
[4, 'tall', 'blue', 13]] 

私は1つの要素でソートしたい場合は、背の高い/ショート要素を言う、私はs = sorted(s, key = itemgetter(1))経由でそれを行うことができます。

もし私がでソートしたければ、それぞれの要素に1回、ソールを2回行うことができますが、速い方法がありますか?

+0

【関連PPCGの質問](http://codegolf.stackexchange.com/q/85321/34718) – mbomb007

答えて

406

キーはタプルを返す関数とすることができる。

s = sorted(s, key = lambda x: (x[1], x[2])) 

それともitemgetterを使用して同じことを達成することができます

import operator 
s = sorted(s, key = operator.itemgetter(1, 2)) 

そして、ここであなたがsortedを使用してsortの代わりに使用することができることを予告再割り当てする:

s.sort(key = operator.itemgetter(1, 2)) 
+2

あなたが学ぶ何か新しい日常のオブジェクトのリストをランク付けする方法です!以前の方法より計算が速いかどうかは分かりますか?それとも、バックグラウンドで同じことをやっているだけですか? – headache

+2

@headache:どちらが速いのかわかりません - 私はそれらがほぼ同じであると思われます。興味があれば 'timeit'モジュールを使って両方のパフォーマンスを測定することができます。 –

+14

timeitからの完全性について:最初にループごとに6 us、ループごとに2番目に4.4 usを与えました。 –

18

私はこれが最もpythonicな方法であるかどうかわからない場合... 整数値を降順に、第2をアルファベット順にソートする必要があるタプルのリストがありました。これは、整数のソートを元に戻す必要がありますが、アルファベット順ではありません。それはあなたの代わりにtuplelistを使用することができます表示されます

a = [('Al', 2),('Bill', 1),('Carol', 2), ('Abel', 3), ('Zeke', 2), ('Chris', 1)] 
b = sorted(sorted(a, key = lambda x : x[0]), key = lambda x : x[1], reverse = True) 
print(b) 
[('Abel', 3), ('Al', 2), ('Carol', 2), ('Zeke', 2), ('Bill', 1), ('Chris', 1)] 
+4

2番目は数値なので、 'b = sorted(a、key = lambda x:(-x [1]、x [0])) 'これはどちらの基準が最初に適用されるかがよりわかりやすい。効率性についてはわかりませんが、誰かがタイムスリップする必要があります。 –

+0

@AndreiPetreあなたの答えは私には非常に意味がありました。そのように覚えやすくコード化するのは簡単です。ありがとうございました。 – Sriram

1

(私は、あなたはできるの巣 "の機能をソートしても気づいていなかった、ところで試験でその場で):ここに私の解決策でした。 これは、リスト/タプルの「マジックインデックス」ではなく属性を取得しているときに、より重要になります。

私のケースでは、クラスの複数の属性(入力キーが文字列であった)で並べ替えることが必要でした。私は別の場所で異なるソートが必要でした。クライアントがやり取りしていた親クラスの共通のデフォルトソートが必要でした。唯一だから、最初、私はヘルパーメソッドを定義しただけでなく、私はクラスが

を共有することができることをリストとしてそれらを格納することができようにして、私は本当に「に必要な」「並べ替えキー」を上書きすること

def attr_sort(self, attrs=['someAttributeString']: 
    '''helper to sort by the attributes named by strings of attrs in order''' 
    return lambda k: [ getattr(k, attr) for attr in attrs ] 

は、それに

# would defined elsewhere but showing here for consiseness 
self.SortListA = ['attrA', 'attrB'] 
self.SortListB = ['attrC', 'attrA'] 
records = .... #list of my objects to sort 
records.sort(key=self.attr_sort(attrs=self.SortListA)) 
# perhaps later nearby or in another function 
more_records = .... #another list 
more_records.sort(key=self.attr_sort(attrs=self.SortListB)) 

を使用するこれはobjectを設け文字列名に対応するゲッターを有すると仮定object.attrAによってリストをソート生成ラムダ関数を使用し、object.attrBう。 2番目のケースでは、object.attrC、次にobject.attrAでソートされます。

これにより、コンシューマ、単体テスト、またはあなたのアプリでいくつかの操作の並べ替えがどのように行われているかを伝えることができます。リストを作成し、バックエンドの実装に結合しないでください。

0

ソート関数のリストを取得するためのソート関数を基本的に書き直します。各ソート関数は、テストする属性を各ソートテストで比較して、cmp関数が返り値を破棄して送信する場合はゼロ以外の戻り値。 Lambdaのリストの関数のLambdaを呼び出して呼び出します。

利点は、他の方法と同じように、以前の並べ替えの種類ではなく、データを1回通過することです。別のことは、ソートされたものがコピーを作成するように見えるのに対して、ソートされていることです。

各オブジェクトがグループ内にあり、スコア関数を持つクラスのリストをランク付けするランク関数を作成するのに使用しましたが、属性のリストを追加できます。 ラムダを使用してセッターを呼び出すのは、ラムダのような、ハックな使い方に注意してください。 ランク部分はリストの配列に対しては機能しませんが、並べ替えは実​​行されます。ここで

#First, here's a pure list version 
my_sortLambdaLst = [lambda x,y:cmp(x[0], y[0]), lambda x,y:cmp(x[1], y[1])] 
def multi_attribute_sort(x,y): 
    r = 0 
    for l in my_sortLambdaLst: 
     r = l(x,y) 
     if r!=0: return r #keep looping till you see a difference 
    return r 

Lst = [(4, 2.0), (4, 0.01), (4, 0.9), (4, 0.999),(4, 0.2), (1, 2.0), (1, 0.01), (1, 0.9), (1, 0.999), (1, 0.2) ] 
Lst.sort(lambda x,y:multi_attribute_sort(x,y)) #The Lambda of the Lambda 
for rec in Lst: print str(rec) 

class probe: 
    def __init__(self, group, score): 
     self.group = group 
     self.score = score 
     self.rank =-1 
    def set_rank(self, r): 
     self.rank = r 
    def __str__(self): 
     return '\t'.join([str(self.group), str(self.score), str(self.rank)]) 


def RankLst(inLst, group_lambda= lambda x:x.group, sortLambdaLst = [lambda x,y:cmp(x.group, y.group), lambda x,y:cmp(x.score, y.score)], SetRank_Lambda = lambda x, rank:x.set_rank(rank)): 
    #Inner function is the only way (I could think of) to pass the sortLambdaLst into a sort function 
    def multi_attribute_sort(x,y): 
     r = 0 
     for l in sortLambdaLst: 
      r = l(x,y) 
      if r!=0: return r #keep looping till you see a difference 
     return r 

    inLst.sort(lambda x,y:multi_attribute_sort(x,y)) 
    #Now Rank your probes 
    rank = 0 
    last_group = group_lambda(inLst[0]) 
    for i in range(len(inLst)): 
     rec = inLst[i] 
     group = group_lambda(rec) 
     if last_group == group: 
      rank+=1 
     else: 
      rank=1 
      last_group = group 
     SetRank_Lambda(inLst[i], rank) #This is pure evil!! The lambda purists are gnashing their teeth 

Lst = [probe(4, 2.0), probe(4, 0.01), probe(4, 0.9), probe(4, 0.999), probe(4, 0.2), probe(1, 2.0), probe(1, 0.01), probe(1, 0.9), probe(1, 0.999), probe(1, 0.2) ] 

RankLst(Lst, group_lambda= lambda x:x.group, sortLambdaLst = [lambda x,y:cmp(x.group, y.group), lambda x,y:cmp(x.score, y.score)], SetRank_Lambda = lambda x, rank:x.set_rank(rank)) 
print '\t'.join(['group', 'score', 'rank']) 
for r in Lst: print r 
関連する問題