Sözlüklerin listesini sözlüğün değerine göre nasıl sıralayabilirim?


1896

Sözlükler listesi var ve her öğenin belirli bir özellik değerlerine göre sıralanmasını istiyorum.

Aşağıdaki diziyi göz önünde bulundurun,

[{'name':'Homer', 'age':39}, {'name':'Bart', 'age':10}]

Sıralama ölçütü name,

[{'name':'Bart', 'age':10}, {'name':'Homer', 'age':39}]

Cevabı okumak ve operator.itemgetter'a bakmak . Aynı süreçte birden fazla değere göre sıralama yapabilir miyim (örneğin, [{'name':'Bart', 'age':10, 'note':3},{'name':'Homer','age':10,'note':2},{'name':'Vasile','age':20,'note':3}] Ve kullanmak için: from operator import itemgetter newlist = sorted(old_list, key=itemgetter(-'note','name') EDIT: Tested, ve çalışıyor ama nasıl DESC ve ASC adını nasıl yapacağımı bilmiyorum.
Claudiu

Yanıtlar:


2467

Bir cmp yerine bir anahtar kullanarak daha temiz görünebilir:

newlist = sorted(list_to_be_sorted, key=lambda k: k['name']) 

veya JFSebastian ve diğerlerinin önerdiği gibi,

from operator import itemgetter
newlist = sorted(list_to_be_sorted, key=itemgetter('name')) 

Tamlık için (fitzgeraldsteele tarafından yapılan yorumlarda belirtildiği gibi), reverse=Trueazalan sıralamaya ekleyin

newlist = sorted(l, key=itemgetter('name'), reverse=True)

34
Anahtar kullanımı sadece daha temiz değil, aynı zamanda daha verimlidir.
jfs

5
En hızlı yol bir newlist.reverse () ifadesi eklemektir. Aksi takdirde cmp = lambda x, y: - cmp (x ['ad'], y ['ad']) gibi bir karşılaştırma tanımlayabilirsiniz.
Mario F

3
sıralama değeri bir sayı ise diyebilirsiniz: lambda k: (k ['yaş'] * -1) ters sıralama elde etmek için
Philluminati

2
Bu, sıralanacak tuple elemanının dizininin itemgetter(i)nerede olduğunu kullanırsanız, bir tuples listesi için de geçerlidir i.
radicand

42
itemgetterbirden fazla argümanı kabul eder: itemgetter(1,2,3)bir demet gibi dönen bir işlevdir obj[1], obj[2], obj[3], bu yüzden karmaşık türler yapmak için kullanabilirsiniz.
Bakuriu

166
import operator

Sözlük listesini key = 'name' ile sıralamak için:

list_of_dicts.sort(key=operator.itemgetter('name'))

Sözlük listesini key = 'age' ile sıralamak için:

list_of_dicts.sort(key=operator.itemgetter('age'))

9
Neyse adı ve yaşı birleştirmek için? (SQL ORDER BY adı, yaşı gibi?)
monojohnny

28
@monojohnny: evet, anahtarın bir demet döndürmesini sağla key=lambda k: (k['name'], k['age']). (veya key=itemgetter('name', 'age')). tuple'lar cmpsırayla her elemanı karşılaştırır. çok kanlı.
Claudiu

1
Belgelerde ( docs.python.org/2/tutorial/datastructures.html ) için isteğe bağlı keyargüman list.sort()açıklanmamıştır. Nerede bulacağına dair bir fikrin var mı?
TTT

2
@TTT: ve arkadaşları için kütüphane belgelerine bakın list.
Kevin

64
my_list = [{'name':'Homer', 'age':39}, {'name':'Bart', 'age':10}]

my_list.sort(lambda x,y : cmp(x['name'], y['name']))

my_list şimdi istediğin şey olacak.

(3 yıl sonra) Eklemek için düzenlendi:

Yeni keyargüman daha verimli ve daha titiz. Şimdi daha iyi bir cevap şuna benziyor:

my_list = sorted(my_list, key=lambda k: k['name'])

... lambda, IMO, anlaşılması daha kolay operator.itemgetter, ama YMMV.


51

Listeyi birden çok tuşa göre sıralamak istiyorsanız aşağıdakileri yapabilirsiniz:

my_list = [{'name':'Homer', 'age':39}, {'name':'Milhouse', 'age':10}, {'name':'Bart', 'age':10} ]
sortedlist = sorted(my_list , key=lambda elem: "%02d %s" % (elem['age'], elem['name']))

Değerleri karşılaştırma için tek bir dize temsiline dönüştürmeye dayalı olduğu için oldukça hacklidir, ancak negatif olanlar da dahil olmak üzere sayılar için beklendiği gibi çalışır (sayıları kullanıyorsanız dizenizi sıfır dolguyla uygun şekilde biçimlendirmeniz gerekir)


2
istikrarlı bir timsort kullanarak sıralanmış, birkaç kriter sıralamak için birkaç kez sıralanmış arayabilirsiniz
njzk2

njzk2 adlı kullanıcının yorumu hemen bana açık değildi, bu yüzden aşağıdakileri buldum. Njzk2'nin önerdiği gibi iki kez sıralayabilir veya üst yanıtta birden fazla argümanı operator.itemgetter'a aktarabilirsiniz. Link: stackoverflow.com/questions/5212870/…
Permafacture

15
Dizeye dönüştürmeye gerek yok. Sadece bir tuple anahtar olarak dönün.
Winston Ewert

Birden çok kez sıralamak,
kesmeksizin

30
import operator
a_list_of_dicts.sort(key=operator.itemgetter('name'))

'key' rastgele bir değere göre sıralamak için kullanılır ve 'itemgetter' bu değeri her bir öğenin 'name' özelliğine ayarlar.


27
a = [{'name':'Homer', 'age':39}, ...]

# This changes the list a
a.sort(key=lambda k : k['name'])

# This returns a new list (a is not modified)
sorted(a, key=lambda k : k['name']) 

21

Sanırım demek istediniz:

[{'name':'Homer', 'age':39}, {'name':'Bart', 'age':10}]

Bu şu şekilde sıralanır:

sorted(l,cmp=lambda x,y: cmp(x['name'],y['name']))

19

Özel bir karşılaştırma işlevi kullanabilir veya özel bir sıralama anahtarı hesaplayan bir işlevi iletebilirsiniz. Anahtar genellikle öğe başına sadece bir kez hesaplandığından, genellikle daha etkilidir; karşılaştırma fonksiyonu ise birçok kez çağrılır.

Bunu şu şekilde yapabilirsiniz:

def mykey(adict): return adict['name']
x = [{'name': 'Homer', 'age': 39}, {'name': 'Bart', 'age':10}]
sorted(x, key=mykey)

Ama standart kütüphane rasgele nesne öğelerini almak için genel bir rutin içerir: itemgetter. Bunun yerine şunu deneyin:

from operator import itemgetter
x = [{'name': 'Homer', 'age': 39}, {'name': 'Bart', 'age':10}]
sorted(x, key=itemgetter('name'))

19

Perl'den Schwartz dönüşümünü kullanarak,

py = [{'name':'Homer', 'age':39}, {'name':'Bart', 'age':10}]

yapmak

sort_on = "name"
decorated = [(dict_[sort_on], dict_) for dict_ in py]
decorated.sort()
result = [dict_ for (key, dict_) in decorated]

verir

>>> result
[{'age': 10, 'name': 'Bart'}, {'age': 39, 'name': 'Homer'}]

Perl Schwartz dönüşüm hakkında daha fazla bilgi

Bilgisayar biliminde Schwartz dönüşüm, bir öğe listesini sıralama etkinliğini artırmak için kullanılan bir Perl programlama deyimidir. Bu deyim, sipariş aslında öğelerin belirli bir özelliğinin (anahtarının) sırasına dayandığı zaman karşılaştırma tabanlı sıralama için uygundur, burada bu özelliğin hesaplanması en az sayıda yapılması gereken yoğun bir işlemdir. Schwartz Dönüşümü, adlandırılmış geçici diziler kullanmaması nedeniyle dikkat çekicidir.


9
Python desteklemiştir key=için .sort, 2004 yılı olduğunu, bu C, sıralama kodundaki Schwartzian dönüşümü yoktur 2.4 beri; bu nedenle bu yöntem sadece Pythons 2.0-2.3 için yararlıdır. hepsi 12 yaşından büyük.
Antti Haapala


12

bazen biz kullanmak gerekir lower()örneğin

lists = [{'name':'Homer', 'age':39},
  {'name':'Bart', 'age':10},
  {'name':'abby', 'age':9}]

lists = sorted(lists, key=lambda k: k['name'])
print(lists)
# [{'name':'Bart', 'age':10}, {'name':'Homer', 'age':39}, {'name':'abby', 'age':9}]

lists = sorted(lists, key=lambda k: k['name'].lower())
print(lists)
# [ {'name':'abby', 'age':9}, {'name':'Bart', 'age':10}, {'name':'Homer', 'age':39}]

11

İşte alternatif genel çözüm - dikte unsurlarını anahtarlara ve değerlere göre sıralar. Avantajı - anahtarları belirtmeye gerek yoktur ve bazı sözlüklerde bazı anahtarlar eksikse yine de çalışır.

def sort_key_func(item):
    """ helper function used to sort list of dicts

    :param item: dict
    :return: sorted list of tuples (k, v)
    """
    pairs = []
    for k, v in item.items():
        pairs.append((k, v))
    return sorted(pairs)
sorted(A, key=sort_key_func)

10

Panda paketini kullanmak başka bir yöntemdir, ancak büyük ölçekli çalışma zamanı, başkaları tarafından önerilen daha geleneksel yöntemlerden çok daha yavaştır:

import pandas as pd

listOfDicts = [{'name':'Homer', 'age':39}, {'name':'Bart', 'age':10}]
df = pd.DataFrame(listOfDicts)
df = df.sort_values('name')
sorted_listOfDicts = df.T.to_dict().values()

Küçük bir liste ve büyük (100k +) dikte listesi için bazı karşılaştırma değerleri şunlardır:

setup_large = "listOfDicts = [];\
[listOfDicts.extend(({'name':'Homer', 'age':39}, {'name':'Bart', 'age':10})) for _ in range(50000)];\
from operator import itemgetter;import pandas as pd;\
df = pd.DataFrame(listOfDicts);"

setup_small = "listOfDicts = [];\
listOfDicts.extend(({'name':'Homer', 'age':39}, {'name':'Bart', 'age':10}));\
from operator import itemgetter;import pandas as pd;\
df = pd.DataFrame(listOfDicts);"

method1 = "newlist = sorted(listOfDicts, key=lambda k: k['name'])"
method2 = "newlist = sorted(listOfDicts, key=itemgetter('name')) "
method3 = "df = df.sort_values('name');\
sorted_listOfDicts = df.T.to_dict().values()"

import timeit
t = timeit.Timer(method1, setup_small)
print('Small Method LC: ' + str(t.timeit(100)))
t = timeit.Timer(method2, setup_small)
print('Small Method LC2: ' + str(t.timeit(100)))
t = timeit.Timer(method3, setup_small)
print('Small Method Pandas: ' + str(t.timeit(100)))

t = timeit.Timer(method1, setup_large)
print('Large Method LC: ' + str(t.timeit(100)))
t = timeit.Timer(method2, setup_large)
print('Large Method LC2: ' + str(t.timeit(100)))
t = timeit.Timer(method3, setup_large)
print('Large Method Pandas: ' + str(t.timeit(1)))

#Small Method LC: 0.000163078308105
#Small Method LC2: 0.000134944915771
#Small Method Pandas: 0.0712950229645
#Large Method LC: 0.0321750640869
#Large Method LC2: 0.0206089019775
#Large Method Pandas: 5.81405615807

3
Kodunuzu çalıştırdı ve Büyük Yöntem Pandalar için timeit.Timer argümanlarında bir hata buldum: "setup_large" olması gereken "setup_small" belirtin. Bu argümanı değiştirmek, programın bitmeden çalışmasına neden oldu ve 5 dakikadan sonra durdurdum. "Timeit (1)" ile çalıştırdığımda, Büyük Yöntem Pandalar LC veya LC2'den çok daha kötü 7.3 saniye içinde bitirdi.
clp2

Oldukça haklısın, bu benim açımdan oldukça büyük bir denetimdi. Artık büyük vakalar için tavsiye etmiyorum! Cevabı basitçe bir olasılık olarak izin vermek için düzenledim, kullanım durumu hala tartışmaya açık.
abby sobh

6

Orijinal gerekmiyorsa listait dictionaries, sizinle yerinde değişiklik olabilir sort()özel bir anahtar işlevi kullanılarak yöntemle.

Anahtar işlevi:

def get_name(d):
    """ Return the value of a key in a dictionary. """

    return d["name"]

listSıralanmasını:

data_one = [{'name': 'Homer', 'age': 39}, {'name': 'Bart', 'age': 10}]

Yerinde sıralama:

data_one.sort(key=get_name)

Orijinaline ihtiyacınız varsa list, sorted()onu geçen işlevi listve anahtar işlevini çağırın , ardından döndürülen diziyi listyeni bir değişkene atayın :

data_two = [{'name': 'Homer', 'age': 39}, {'name': 'Bart', 'age': 10}]
new_data = sorted(data_two, key=get_name)

Baskı data_oneve new_data.

>>> print(data_one)
[{'name': 'Bart', 'age': 10}, {'name': 'Homer', 'age': 39}]
>>> print(new_data)
[{'name': 'Bart', 'age': 10}, {'name': 'Homer', 'age': 39}]

6

Diyelim ki Daşağıdaki öğeleri içeren bir sözlüğüm var. Sıralamak için özel işlevi aşağıdaki gibi geçirmek üzere sıralanmış anahtar tuşunu kullanın:

D = {'eggs': 3, 'ham': 1, 'spam': 2}
def get_count(tuple):
    return tuple[1]

sorted(D.items(), key = get_count, reverse=True)
# or
sorted(D.items(), key = lambda x: x[1], reverse=True)  # avoiding get_count function call

Kontrol bu out.


3

Lambda filtre w / büyük bir hayranı olmuştur ancak zaman karmaşıklığı göz önüne alındığında en iyi seçenek değil

İlk seçenek

sorted_list = sorted(list_to_sort, key= lambda x: x['name'])
# returns list of values

İkinci seçenek

list_to_sort.sort(key=operator.itemgetter('name'))
#edits the list, does not return a new list

Yürütme sürelerinin hızlı karşılaştırması

# First option
python3.6 -m timeit -s "list_to_sort = [{'name':'Homer', 'age':39}, {'name':'Bart', 'age':10}, {'name':'Faaa', 'age':57}, {'name':'Errr', 'age':20}]" -s "sorted_l=[]" "sorted_l = sorted(list_to_sort, key=lambda e: e['name'])"

1000000 döngü, döngü başına en iyi 3: 0,736 usec

# Second option 
python3.6 -m timeit -s "list_to_sort = [{'name':'Homer', 'age':39}, {'name':'Bart', 'age':10}, {'name':'Faaa', 'age':57}, {'name':'Errr', 'age':20}]" -s "sorted_l=[]" -s "import operator" "list_to_sort.sort(key=operator.itemgetter('name'))"

1000000 döngü, döngü başına en iyi 3: 0,438 usec


2

Performans bir sorun olacaksa, ben kullanırım operator.itemgetteryerine lambdadaha hızlı el yapımı fonksiyonlarından daha gerçekleştirmek yerleşik işlevler gibi. itemgetterFonksiyon göre yaklaşık% 20 daha hızlı gerçekleştirmek gibi görünüyor lambdabenim testlere dayanmaktadır.

Gönderen https://wiki.python.org/moin/PythonSpeed :

Benzer şekilde, yerleşik işlevler el yapımı eşdeğerlerden daha hızlı çalışır. Örneğin, harita (operatör.ekle, v1, v2) haritadan daha hızlıdır (lambda x, y: x + y, v1, v2).

İşte lambdavs kullanarak sıralama hızı karşılaştırması itemgetter.

import random
import operator

# create a list of 100 dicts with random 8-letter names and random ages from 0 to 100.
l = [{'name': ''.join(random.choices(string.ascii_lowercase, k=8)), 'age': random.randint(0, 100)} for i in range(100)]

# Test the performance with a lambda function sorting on name
%timeit sorted(l, key=lambda x: x['name'])
13 µs ± 388 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

# Test the performance with itemgetter sorting on name
%timeit sorted(l, key=operator.itemgetter('name'))
10.7 µs ± 38.1 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

# Check that each technique produces same sort order
sorted(l, key=lambda x: x['name']) == sorted(l, key=operator.itemgetter('name'))
True

Her iki teknik de listeyi aynı sırada sıralar (kod bloğundaki son ifadenin çalıştırılmasıyla doğrulanır), ancak biri biraz daha hızlıdır.


-1

Aşağıdaki kodu kullanabilirsiniz

sorted_dct = sorted(dct_name.items(), key = lambda x : x[1])
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.