Bagaimana cara memeriksa apakah semua elemen daftar cocok dengan suatu kondisi?

208

Saya memiliki daftar yang terdiri dari 20.000 daftar. Saya menggunakan elemen ke-3 daftar masing-masing sebagai bendera. Saya ingin melakukan beberapa operasi pada daftar ini selama setidaknya satu flag elemen adalah 0, seperti:

my_list = [["a", "b", 0], ["c", "d", 0], ["e", "f", 0], .....]

Pada awalnya, semua flag adalah 0. Saya menggunakan loop sementara untuk memeriksa apakah setidaknya satu flag elemen adalah 0:

def check(list_):
    for item in list_:
        if item[2] == 0:
            return True
    return False

Jika check(my_list)kembali True, maka saya terus mengerjakan daftar saya:

while check(my_list):
    for item in my_list:
        if condition:
            item[2] = 1
        else:
            do_sth()

Sebenarnya, saya ingin menghapus elemen di my_list saat saya mengulanginya, tapi saya tidak diizinkan untuk menghapus item karena saya mengulanginya.

My_list asli tidak memiliki bendera:

my_list = [["a", "b"], ["c", "d"], ["e", "f"], .....]

Karena saya tidak bisa menghapus elemen ketika saya mengulanginya, saya membuat flag-flag ini. Tetapi yang my_listberisi banyak item, dan whileloop membaca semuanya pada setiap forloop, dan itu menghabiskan banyak waktu! Apakah Anda punya saran?

alwbtc
sumber
3
Sepertinya struktur data Anda tidak ideal untuk masalah Anda. Jika Anda sedikit menjelaskan konteksnya, mungkin kami dapat menyarankan sesuatu yang lebih sesuai.
uselpa
Mungkin Anda bisa mengganti item dengan Noneatau []saat Anda mengulangi daftar alih-alih menghapusnya. Memeriksa seluruh daftar dengan 'check () `iterasi pada semua item sebelum setiap pass pada loop dalam adalah pendekatan yang sangat lambat.
martineau

Jawaban:

403

Jawaban terbaik di sini adalah menggunakan all(), yang merupakan dasar untuk situasi ini. Kami menggabungkan ini dengan ekspresi generator untuk menghasilkan hasil yang Anda inginkan dengan bersih dan efisien. Sebagai contoh:

>>> items = [[1, 2, 0], [1, 2, 0], [1, 2, 0]]
>>> all(flag == 0 for (_, _, flag) in items)
True
>>> items = [[1, 2, 0], [1, 2, 1], [1, 2, 0]]
>>> all(flag == 0 for (_, _, flag) in items)
False

Catatan yang all(flag == 0 for (_, _, flag) in items)secara langsung setara dengan all(item[2] == 0 for item in items), hanya sedikit lebih baik untuk dibaca dalam hal ini.

Dan, untuk contoh filter, pemahaman daftar (tentu saja, Anda bisa menggunakan ekspresi generator yang sesuai):

>>> [x for x in items if x[2] == 0]
[[1, 2, 0], [1, 2, 0]]

Jika Anda ingin memeriksa setidaknya satu elemen bernilai 0, opsi yang lebih baik adalah menggunakan any()yang lebih mudah dibaca:

>>> any(flag == 0 for (_, _, flag) in items)
True
Gareth Latty
sumber
Kesalahan saya pada penggunaan lambda, Python semua tidak menerima fungsi sebagai argumen pertama seperti Haskell et. al., saya mengubah jawaban saya menjadi daftar pemahaman juga. :)
Hampus Nilsson
3
@HampusNilsson Pemahaman daftar tidak sama dengan ekspresi generator. Sebagai all()dan any()hubungan pendek, jika, misalnya, nilai pertama pada tambang dievaluasi False, all()akan gagal dan tidak memeriksa nilai lagi, kembali False. Contoh Anda akan melakukan hal yang sama, kecuali itu akan menghasilkan seluruh daftar perbandingan terlebih dahulu, yang berarti banyak pemrosesan tanpa hasil.
Gareth Latty
14

Jika Anda ingin memeriksa apakah ada item dalam daftar yang melanggar kondisi, gunakan all:

if all([x[2] == 0 for x in lista]):
    # Will run if all elements in the list has x[2] = 0 (use not to invert if necessary)

Untuk menghapus semua elemen yang tidak cocok, gunakan filter

# Will remove all elements where x[2] is 0
listb = filter(lambda x: x[2] != 0, listb)
Hampus Nilsson
sumber
2
Anda dapat menghapus [...]di all(...)karena dapat membuat generator bukannya daftar, yang tidak hanya menghemat dua karakter tetapi juga menghemat memori dan waktu. Dengan menggunakan generator, hanya satu item yang akan dihitung pada satu waktu (hasil sebelumnya akan dihapus karena tidak lagi digunakan) dan jika salah satu dari mereka ternyata False, generator akan berhenti menghitung sisanya.
InQβ
7

Anda dapat menggunakan waktu itertools seperti ini, itu akan berhenti setelah kondisi terpenuhi yang gagal pernyataan Anda. Metode yang berlawanan adalah dropwhile

for x in itertools.takewhile(lambda x: x[2] == 0, list)
    print x
Hedde van der Heide
sumber
0

Cara lain untuk digunakan itertools.ifilter. Ini memeriksa kebenaran dan proses (menggunakanlambda )

Sampel-

for x in itertools.ifilter(lambda x: x[2] == 0, my_list):
    print x
SIslam
sumber
0

cara ini sedikit lebih fleksibel daripada menggunakan all():

my_list = [[1, 2, 0], [1, 2, 0], [1, 2, 0]]
all_zeros = False if False in [x[2] == 0 for x in my_list] else True
any_zeros = True if True in [x[2] == 0 for x in my_list] else False

atau lebih ringkas:

all_zeros = not False in [x[2] == 0 for x in my_list]
any_zeros = 0 in [x[2] for x in my_list]
mulllhausen
sumber
Tidak bisakah Anda hanya mengatakan all_zeros = False in [x[2] == 0 for x in my_list]atau bahkan 0 in [x[2] for x in my_list]dan sesuai untuk any_zeros? Saya tidak benar-benar melihat peningkatan luar biasa all().
tripleee
tidak, versi Anda - all_zeros = False in [x[2] == 0 for x in my_list]dievaluasi menjadi False, sedangkan versi saya dievaluasi True. Jika Anda mengubahnya all_zeros = not (False in [x[2] == 0 for x in my_list])maka setara dengan milikku. Dan 0 in [x[2] for x in my_list]jelas hanya akan berhasil any_zeros. Tapi saya suka ringkasnya ide Anda, jadi saya akan memperbarui jawaban saya
mulllhausen