Làm thế nào để kiểm tra nếu tất cả các yếu tố của một danh sách phù hợp với một điều kiện?


208

Tôi có một danh sách bao gồm khoảng 20000 danh sách. Tôi sử dụng phần tử thứ 3 của mỗi danh sách làm cờ. Tôi muốn thực hiện một số thao tác trong danh sách này miễn là ít nhất một cờ của phần tử là 0, giống như:

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

Ban đầu, tất cả các cờ đều bằng 0. Tôi sử dụng vòng lặp while để kiểm tra xem ít nhất một cờ của phần tử có bằng 0 hay không:

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

Nếu check(my_list)trả về True, sau đó tôi tiếp tục làm việc trong danh sách của mình:

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

Trên thực tế, tôi muốn xóa một phần tử trong my_list khi tôi lặp lại phần tử đó, nhưng tôi không được phép xóa các mục khi tôi lặp lại phần tử đó.

Bản gốc my_list không có cờ:

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

Vì tôi không thể loại bỏ các yếu tố khi tôi lặp lại nó, tôi đã phát minh ra những lá cờ này. Nhưng trong đó my_listcó nhiều mục và whilevòng lặp đọc tất cả chúng ở mỗi forvòng lặp và nó tiêu tốn rất nhiều thời gian! Bạn có đề nghị nào không?


3
Có vẻ như cấu trúc dữ liệu của bạn không lý tưởng cho vấn đề của bạn. Nếu bạn giải thích bối cảnh nhiều hơn một chút, có lẽ chúng tôi có thể đề xuất một cái gì đó phù hợp hơn.
uselpa

Có lẽ bạn có thể thay thế các mục bằng Nonehoặc []khi bạn lặp qua danh sách thay vì xóa chúng. Kiểm tra toàn bộ danh sách với 'check () `lặp qua tất cả các mục trước mỗi lần vượt qua trên vòng lặp bên trong là một cách tiếp cận rất chậm.
martineau

Câu trả lời:


402

Câu trả lời tốt nhất ở đây là sử dụng all(), đó là nội dung cho tình huống này. Chúng tôi kết hợp điều này với một biểu thức trình tạo để tạo ra kết quả bạn muốn sạch sẽ và hiệu quả. Ví dụ:

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

Lưu ý rằng all(flag == 0 for (_, _, flag) in items)tương đương trực tiếp với all(item[2] == 0 for item in items), nó chỉ đẹp hơn một chút để đọc trong trường hợp này.

Và, đối với ví dụ về bộ lọc, việc hiểu danh sách (tất nhiên, bạn có thể sử dụng biểu thức trình tạo khi thích hợp):

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

Nếu bạn muốn kiểm tra ít nhất một phần tử là 0, tùy chọn tốt hơn là sử dụng phần tử any()nào dễ đọc hơn:

>>> any(flag == 0 for (_, _, flag) in items)
True

Lỗi của tôi về việc sử dụng lambda, tất cả Python không chấp nhận một hàm làm đối số đầu tiên như Haskell et. al., tôi đã thay đổi câu trả lời của tôi thành một sự hiểu biết danh sách là tốt. :)
Hampus Nilsson

3
@HampusNilsson Một cách hiểu danh sách không giống như một biểu thức trình tạo. Như all()any()ngắn mạch, ví dụ, nếu giá trị đầu tiên trên mỏ của tôi ước tính False, all()sẽ thất bại và không kiểm tra thêm bất kỳ giá trị nào, trả về False. Ví dụ của bạn sẽ làm tương tự, ngoại trừ nó sẽ tạo ra toàn bộ danh sách so sánh trước, có nghĩa là rất nhiều xử lý không có gì.
Gareth Latty

14

Nếu bạn muốn kiểm tra xem có mục nào trong danh sách vi phạm điều kiện sử dụng không 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)

Để loại bỏ tất cả các yếu tố không phù hợp, sử dụng filter

# Will remove all elements where x[2] is 0
listb = filter(lambda x: x[2] != 0, listb)

2
Bạn có thể loại bỏ [...]trong all(...)vì nó sau đó có thể tạo ra một máy phát điện thay vì một danh sách, trong đó không chỉ giúp bạn tiết kiệm hai nhân vật mà còn giúp tiết kiệm bộ nhớ và thời gian. Bằng cách sử dụng máy phát điện, chỉ có một mục sẽ được tính tại một thời điểm (kết quả cũ sẽ bị loại bỏ do không còn sử dụng nữa) và nếu bất kỳ mục nào trong số chúng bị tắt False, trình tạo sẽ ngừng tính toán phần còn lại.
InQβ

7

Bạn có thể sử dụng itertools như thế này, nó sẽ dừng lại khi một điều kiện được đáp ứng mà không tuyên bố của bạn. Phương pháp ngược lại sẽ là thả

for x in itertools.takewhile(lambda x: x[2] == 0, list)
    print x

0

Một cách khác để sử dụng itertools.ifilter. Điều này kiểm tra tính trung thực và quy trình (sử dụng lambda)

Mẫu vật-

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

0

cách này linh hoạt hơn một chút so với sử dụng 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

hoặc ngắn gọn hơn:

all_zeros = not False in [x[2] == 0 for x in my_list]
any_zeros = 0 in [x[2] for x in my_list]

Bạn không thể nói đơn giản all_zeros = False in [x[2] == 0 for x in my_list]hay thậm chí 0 in [x[2] for x in my_list]và tương ứng cho any_zeros? Tôi thực sự không thấy bất kỳ cải thiện đáng chú ý nào all().
tripleee

không, phiên bản của bạn - all_zeros = False in [x[2] == 0 for x in my_list]ước tính False, trong khi phiên bản của tôi đánh giá True. Nếu bạn thay đổi nó all_zeros = not (False in [x[2] == 0 for x in my_list])thì nó tương đương với tôi. Và 0 in [x[2] for x in my_list]rõ ràng là chỉ đi làm any_zeros. Nhưng tôi thích sự cô đọng trong ý tưởng của bạn, vì vậy tôi sẽ cập nhật câu trả lời của mình
mulllhausen
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.