Xóa chuỗi trống khỏi danh sách chuỗi


682

Tôi muốn xóa tất cả các chuỗi trống khỏi danh sách các chuỗi trong python.

Ý tưởng của tôi trông như thế này:

while '' in str_list:
    str_list.remove('')

Có cách nào pythonic hơn để làm điều này?


45
@Ivo, cả hai câu đó đều không đúng. Bạn không bao giờ nên sửa đổi một danh sách lặp đi lặp lại bằng cách sử dụng for x in listNếu bạn đang sử dụng while loopthì không sao. vòng lặp được trình diễn sẽ loại bỏ các chuỗi rỗng cho đến khi không còn chuỗi trống nào nữa rồi dừng lại. Tôi thực sự thậm chí đã không nhìn vào câu hỏi (chỉ tiêu đề) nhưng tôi đã trả lời với cùng một vòng lặp như một khả năng! Nếu bạn không muốn sử dụng hiểu hoặc bộ lọc vì bộ nhớ, thì đó là một giải pháp rất hay.
aaronasterling

4
Vẫn là một điểm rất hợp lệ để không bao giờ thay đổi danh sách bạn đang lặp đi lặp lại :)
Eduard Luca

1
@EduardLuca nếu quan điểm lặp lại trong danh sách là thay đổi nó, thì điều đó ngược lại với những gì bạn nên làm. Bạn chỉ cần cẩn thận rằng bạn biết rằng bạn không gây ra hành vi bất ngờ bằng cách làm như vậy.
JFA

1
@EduardLuca, @JFA: Vấn đề là anh ấy KHÔNG lặp lại bất kỳ danh sách nào. Anh ta sẽ làm nếu anh ta đã viết một cái gì đó trong mẫu for var in list:, nhưng ở đây, anh ta đã viết while const in list:. mà không lặp đi lặp lại bất cứ điều gì. nó chỉ lặp lại cùng một mã cho đến khi một điều kiện là sai.
Camion

Câu trả lời:


1150

Tôi sẽ sử dụng filter:

str_list = filter(None, str_list)
str_list = filter(bool, str_list)
str_list = filter(len, str_list)
str_list = filter(lambda item: item, str_list)

Python 3 trả về một iterator từ đó filter, vì vậy nên được gọi trong một cuộc gọi đếnlist()

str_list = list(filter(None, str_list))

11
Nếu bạn ép cho hiệu suất, itertool'sifilter thậm chí còn faster- >>> timeit('filter(None, str_list)', 'str_list=["a"]*1000', number=100000) 2.3468542098999023; >>> timeit('itertools.ifilter(None, str_list)', 'str_list=["a"]*1000', number=100000) 0.04442191123962402.
Humphrey Bogart

4
@cpburnz Rất đúng. Tuy nhiên, với ifilterkết quả được đánh giá một cách lười biếng, không phải trong một lần, tôi cho rằng đối với hầu hết các trường hợp ifilterlà tốt hơn. Điều thú vị là việc sử dụng filtervẫn nhanh hơn việc gói một ifiltercái listmặc dù.
Humphrey Bogart

3
Nếu bạn làm điều này với một danh sách các số, lưu ý rằng các số 0 cũng sẽ bị xóa (lưu ý: Tôi chỉ sử dụng 3 phương thức đầu tiên), vì vậy bạn sẽ cần một phương pháp thay thế.
SnoringFrog

2
Điều này chỉ tập trung vào tốc độ, không phải là cách giải pháp pythonic (câu hỏi đã được hỏi). Danh sách hiểu là giải pháp pythonic và bộ lọc chỉ nên được sử dụng nếu hồ sơ đã chứng minh rằng listcomp là một nút cổ chai.
Tritium21

3
@ whoever-mentions-about-or-imply-Python-3, vui lòng chỉ cần chỉnh sửa và cập nhật câu trả lời. Chúng tôi chỉ thảo luận về Python 2 khi câu hỏi này được hỏi, thậm chí Python 3 đã được phát hành gần 2 năm. Nhưng hãy cập nhật cả kết quả Python 2 và 3.
livibetter

236

Sử dụng một sự hiểu biết danh sách là cách Pythonic nhất:

>>> strings = ["first", "", "second"]
>>> [x for x in strings if x]
['first', 'second']

Nếu danh sách phải được sửa đổi tại chỗ, bởi vì có các tham chiếu khác phải xem dữ liệu được cập nhật, sau đó sử dụng phép gán lát:

strings[:] = [x for x in strings if x]

16
Tôi thích giải pháp này vì nó dễ dàng thích nghi. Nếu tôi cần phải loại bỏ không chỉ các chuỗi trống mà cả các chuỗi chỉ là khoảng trắng, ví dụ : [x for x in strings if x.strip()].
Trái phiếu

67

bộ lọc thực sự có một tùy chọn đặc biệt cho việc này:

filter(None, sequence)

Nó sẽ lọc ra tất cả các yếu tố đánh giá thành Sai. Không cần phải sử dụng một cuộc gọi thực tế ở đây như bool, len và như vậy.

Nó cũng nhanh như bản đồ (bool, ...)


5
Đây là một thành ngữ python, trên thực tế. Đó cũng là lần duy nhất tôi vẫn sử dụng bộ lọc (), việc hiểu danh sách đã chiếm lĩnh mọi nơi khác.
kaleissin

24
>>> lstr = ['hello', '', ' ', 'world', ' ']
>>> lstr
['hello', '', ' ', 'world', ' ']

>>> ' '.join(lstr).split()
['hello', 'world']

>>> filter(None, lstr)
['hello', ' ', 'world', ' ']

So sánh thời gian

>>> from timeit import timeit
>>> timeit('" ".join(lstr).split()', "lstr=['hello', '', ' ', 'world', ' ']", number=10000000)
4.226747989654541
>>> timeit('filter(None, lstr)', "lstr=['hello', '', ' ', 'world', ' ']", number=10000000)
3.0278358459472656

Lưu ý rằng filter(None, lstr)không loại bỏ các chuỗi trống với một khoảng trắng ' ', nó chỉ cắt đi ''trong khi ' '.join(lstr).split()loại bỏ cả hai.

Để sử dụng filter()với các chuỗi khoảng trắng bị loại bỏ, phải mất nhiều thời gian hơn:

>>> timeit('filter(None, [l.replace(" ", "") for l in lstr])', "lstr=['hello', '', ' ', 'world', ' ']", number=10000000)
18.101892948150635

nó sẽ không hoạt động nếu bạn có khoảng trắng trong chuỗi từ. ví dụ: ['xin chào thế giới', '', 'xin chào', '']. >> ['hellowworld', '', 'xin chào', ''] bạn có giải pháp nào khác để giữ khoảng trống trong một mục trong danh sách nhưng loại bỏ các mục khác không?
Reihan_amn

Lưu ý rằng filter(None, lstr)không xóa chuỗi trống bằng dấu cách' ' Yeah, vì đó không phải là chuỗi trống.
AMC

15

Trả lời từ @ Ib33X là tuyệt vời. Nếu bạn muốn loại bỏ mọi chuỗi trống, sau khi tước. bạn cần phải sử dụng phương pháp dải quá. Nếu không, nó cũng sẽ trả về chuỗi trống nếu nó có khoảng trắng. Giống như, "" cũng sẽ hợp lệ cho câu trả lời đó. Vì vậy, có thể đạt được bằng cách.

strings = ["first", "", "second ", " "]
[x.strip() for x in strings if x.strip()]

Câu trả lời cho điều này sẽ là ["first", "second"].
Nếu bạn muốn sử dụng filterphương pháp thay thế, bạn có thể làm như thế
list(filter(lambda item: item.strip(), strings)). Đây là kết quả tương tự.


12

Thay vì if x, tôi sẽ sử dụng if X! = '' Để loại bỏ các chuỗi trống. Như thế này:

str_list = [x for x in str_list if x != '']

Điều này sẽ bảo vệ Không có loại dữ liệu trong danh sách của bạn. Ngoài ra, trong trường hợp danh sách của bạn có số nguyên và 0 là một trong số đó, nó cũng sẽ được giữ nguyên.

Ví dụ,

str_list = [None, '', 0, "Hi", '', "Hello"]
[x for x in str_list if x != '']
[None, 0, "Hi", "Hello"]

2
Nếu danh sách của bạn có các loại khác nhau (trừ Không có), bạn có thể gặp vấn đề lớn hơn.
Tritium21

Loại nào? Tôi đã thử với int và các kiểu số khác, chuỗi, danh sách, tupes, bộ và Không có và không có vấn đề ở đó. Tôi có thể thấy rằng nếu có bất kỳ loại người dùng nào xác định không hỗ trợ phương thức str có thể gây ra sự cố. Tôi có nên lo lắng về bất kỳ khác?
thiruvenkadam 23/2/2015

1
Nếu bạn có một str_list = [None, '', 0, "Hi", '', "Hello"], đó là một dấu hiệu của một ứng dụng được thiết kế kém. Bạn không nên có nhiều giao diện (loại) và Không có trong cùng một danh sách.
Tritium21

3
Lấy dữ liệu từ db? danh sách các đối số cho một chức năng trong khi làm kiểm tra tự động?
thiruvenkadam 24/2/2015

3
Đó thường là những bộ dữ liệu.
Tritium21

7

Tùy thuộc vào kích thước danh sách của bạn, nó có thể hiệu quả nhất nếu bạn sử dụng list.remove () thay vì tạo danh sách mới:

l = ["1", "", "3", ""]

while True:
  try:
    l.remove("")
  except ValueError:
    break

Điều này có lợi thế là không tạo ra một danh sách mới, nhưng nhược điểm của việc phải tìm kiếm từ đầu mỗi lần, mặc dù không giống như sử dụng while '' in lnhư đề xuất ở trên, nó chỉ yêu cầu tìm kiếm một lần mỗi lần xuất hiện ''(chắc chắn có một cách để giữ tốt nhất cả hai phương pháp, nhưng nó phức tạp hơn).


1
Bạn có thể chỉnh sửa danh sách tại chỗ bằng cách làm ary[:] = [e for e in ary if e]. Sạch hơn nhiều và không sử dụng ngoại lệ cho luồng điều khiển.
Krzysztof Karski

2
Chà, điều đó không thực sự "đúng chỗ" - tôi khá chắc chắn điều này sẽ tạo ra một danh sách mới và chỉ gán nó cho tên cũ.
Andrew Jaffe

Điều này thực hiện rất kém khi đuôi dữ liệu bị xáo trộn trong bộ nhớ trên mỗi lần xóa. Tốt hơn để loại bỏ tất cả trong một hit.
Wim

7

Hãy nhớ rằng nếu bạn muốn giữ khoảng trắng trong một chuỗi , bạn có thể vô tình xóa chúng bằng cách sử dụng một số cách tiếp cận. Nếu bạn có danh sách này

['xin chào thế giới', '', '', 'xin chào'] những gì bạn có thể muốn ['xin chào thế giới', 'xin chào']

đầu tiên cắt danh sách để chuyển đổi bất kỳ loại khoảng trắng thành chuỗi trống:

space_to_empty = [x.strip() for x in _text_list]

sau đó loại bỏ chuỗi rỗng khỏi danh sách chúng

space_clean_list = [x for x in space_to_empty if x]

nếu bạn muốn giữ khoảng trắng trong một chuỗi, bạn có thể vô tình xóa chúng bằng cách sử dụng một số cách tiếp cận. Thích cách tiếp cận này thì sao?
AMC

Cảm ơn anh bạn, nó đã làm việc cho tôi với một chút thay đổi. tức làspace_clean_list = [x.strip() for x in y if x.strip()]
Muhammad Mehran Khan Attari

6

Sử dụng filter:

newlist=filter(lambda x: len(x)>0, oldlist) 

Hạn chế của việc sử dụng bộ lọc như đã chỉ ra là nó chậm hơn các lựa chọn thay thế; cũng thế,lambda thường là tốn kém.

Hoặc bạn có thể đi đơn giản nhất và lặp đi lặp lại nhiều nhất:

# I am assuming listtext is the original list containing (possibly) empty items
for item in listtext:
    if item:
        newlist.append(str(item))
# You can remove str() based on the content of your original list

đây là phương pháp trực quan nhất và thực hiện nó trong thời gian tốt.


9
Chào mừng đến với SO. Bạn đã không được bỏ qua. Bạn đã không bị tấn công bởi một downvoter bất kỳ. Bạn đã được phản hồi. Khuếch đại: Đối số đầu tiên được đề xuất cho bộ lọc của bạn tệ hơn so với lambda x: len(x)điều tồi tệ hơn lambda x : xlà giải pháp tồi tệ nhất trong 4 giải pháp được chọn. Chức năng chính xác được ưa thích, nhưng không đủ. Di con trỏ qua nút downvote: thông báo "Câu trả lời này không hữu ích".
John Machin

5

Theo báo cáo của Aziz Alto filter(None, lstr) không xóa chuỗi trống bằng dấu cách ' 'nhưng nếu bạn chắc chắn lstr chỉ chứa chuỗi bạn có thể sử dụngfilter(str.strip, lstr)

>>> lstr = ['hello', '', ' ', 'world', ' ']
>>> lstr
['hello', '', ' ', 'world', ' ']
>>> ' '.join(lstr).split()
['hello', 'world']
>>> filter(str.strip, lstr)
['hello', 'world']

So sánh thời gian trên máy tính của tôi

>>> from timeit import timeit
>>> timeit('" ".join(lstr).split()', "lstr=['hello', '', ' ', 'world', ' ']", number=10000000)
3.356455087661743
>>> timeit('filter(str.strip, lstr)', "lstr=['hello', '', ' ', 'world', ' ']", number=10000000)
5.276503801345825

Giải pháp nhanh nhất để loại bỏ ''và chuỗi trống với một khoảng trống ' 'vẫn còn ' '.join(lstr).split().

Như đã báo cáo trong một bình luận, tình huống sẽ khác nếu chuỗi của bạn chứa khoảng trắng.

>>> lstr = ['hello', '', ' ', 'world', '    ', 'see you']
>>> lstr
['hello', '', ' ', 'world', '    ', 'see you']
>>> ' '.join(lstr).split()
['hello', 'world', 'see', 'you']
>>> filter(str.strip, lstr)
['hello', 'world', 'see you']

Bạn có thể thấy rằng filter(str.strip, lstr)bảo tồn các chuỗi có khoảng trắng trên đó nhưng ' '.join(lstr).split()sẽ phân tách chuỗi này.


1
Điều này chỉ hoạt động nếu chuỗi của bạn không chứa khoảng trắng. Nếu không, bạn cũng đang chia những chuỗi đó.
phillyslick

1
@BenPolinsky như bạn đã báo cáo joingiải pháp sẽ chia chuỗi với không gian nhưng bộ lọc thì không. Cảm ơn bạn đã bình luận tôi đã cải thiện câu trả lời của tôi.
Paolo Melchiorre

-1

Tổng hợp các câu trả lời hay nhất:

1. Loại bỏ các emtpties mà không tước:

Đó là, tất cả các chuỗi không gian được giữ lại:

slist = list(filter(None, slist))

CHUYÊN NGHIỆP:

  • đơn giản nhất;
  • nhanh nhất (xem điểm chuẩn bên dưới).

2. Để loại bỏ trống sau khi tước ...

2.a ... khi các chuỗi KHÔNG chứa khoảng trắng giữa các từ:

slist = ' '.join(slist).split()

CHUYÊN NGHIỆP:

  • mã nhỏ
  • nhanh (NHƯNG không nhanh nhất với các bộ dữ liệu lớn do bộ nhớ, trái với kết quả @ paolo-melchiorre)

2.b ... khi chuỗi chứa khoảng trắng giữa các từ?

slist = list(filter(str.strip, slist))

CHUYÊN NGHIỆP:

  • nhanh nhất;
  • sự hiểu biết của mã.

Điểm chuẩn trên máy 2018:

## Build test-data
#
import random, string
nwords = 10000
maxlen = 30
null_ratio = 0.1
rnd = random.Random(0)                  # deterministic results
words = [' ' * rnd.randint(0, maxlen)
         if rnd.random() > (1 - null_ratio)
         else
         ''.join(random.choices(string.ascii_letters, k=rnd.randint(0, maxlen)))
         for _i in range(nwords)
        ]

## Test functions
#
def nostrip_filter(slist):
    return list(filter(None, slist))

def nostrip_comprehension(slist):
    return [s for s in slist if s]

def strip_filter(slist):
    return list(filter(str.strip, slist))

def strip_filter_map(slist): 
    return list(filter(None, map(str.strip, slist))) 

def strip_filter_comprehension(slist):  # waste memory
    return list(filter(None, [s.strip() for s in slist]))

def strip_filter_generator(slist):
    return list(filter(None, (s.strip() for s in slist)))

def strip_join_split(slist):  # words without(!) spaces
    return ' '.join(slist).split()

## Benchmarks
#
%timeit nostrip_filter(words)
142 µs ± 16.8 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

%timeit nostrip_comprehension(words)
263 µs ± 19.1 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

%timeit strip_filter(words)
653 µs ± 37.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

%timeit strip_filter_map(words)
642 µs ± 36 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

%timeit strip_filter_comprehension(words)
693 µs ± 42.2 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

%timeit strip_filter_generator(words)
750 µs ± 28.6 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

%timeit strip_join_split(words)
796 µs ± 103 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

s and s.strip()có thể được đơn giản hóa để chỉ s.strip().
AMC

s and s.strip()là cần thiết nếu chúng ta muốn nhân rộng hoàn toàn filter(None, words), câu trả lời được chấp nhận. Tôi đã sửa các hàm mẫu x2 ở trên và bỏ x2 các hàm xấu.
ankostis

-2

Đối với danh sách có sự kết hợp của khoảng trắng và giá trị trống, hãy sử dụng cách hiểu danh sách đơn giản -

>>> s = ['I', 'am', 'a', '', 'great', ' ', '', '  ', 'person', '!!', 'Do', 'you', 'think', 'its', 'a', '', 'a', '', 'joke', '', ' ', '', '?', '', '', '', '?']

Vì vậy, bạn có thể thấy, danh sách này có sự kết hợp của các khoảng trắng và các phần tử null. Sử dụng đoạn trích -

>>> d = [x for x in s if x.strip()]
>>> d
>>> d = ['I', 'am', 'a', 'great', 'person', '!!', 'Do', 'you', 'think', 'its', 'a', 'a', 'joke', '?', '?']
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.