簡體   English   中英

如何從列表中刪除最后一次出現的項目?

[英]How to remove the last occurrence of an item from a list?

list.remove() 函數用於刪除第一次出現在列表中的項目。 有沒有內置函數可以刪除最后一次? 例如,如果我有一個列表,請說:

X = ['sf', 'cc', 'ch', 'sc', 'sh', 'ch']

並且我想從列表中刪除最后一個“ch”,是否有比我目前正在做的更好的方法,即:

X.reverse()
X.remove('ch')
X.reverse()

我很快也將不得不擔心被刪除的項目可能不在列表中的情況。 因此,在這種情況下不會拋出錯誤的方法將是首選。

if 'ch' in X:
    X.reverse()
    X.remove('ch')
    X.reverse()

最pythonic的方法是try: except remove:

X.reverse()
try:
    X.remove('ch')
except:
    pass
X.reverse()

根據您對速度的評論,這兩種方法都是 O(N),因為x in listlist.reverse()都是 O(N),因此它們之間沒有太多關系。 如果您希望元素通常在那里,您可以使用 try: catch 將x in list保存x in list檢查中,但是如果您希望它通常不在那里,您可以通過首先檢查成員資格來保存 2 個reverse() s。

你的代碼真的沒有任何問題。 它有效,很清楚它為什么有效,很難出錯或誤解。

是的,你可以讓它更快,但只能通過一個常數因子。 (你的算法做了兩個reverse s,每個N步,一個remove ,這是N-1步,所以O(N) 。由於你的數據沒有排序或任何可以幫助我們更快地找到值的東西,很明顯,理想的算法也是O(N) 。)並且以使其更復雜為代價。

顯然可能更快的方法是從末尾手動迭代,直到找到一個值,然后刪除該值。 這也避免了必須處理ValueError 使用enumerate可能會有所幫助……但正確使用(不復制整個內容)可能會很棘手。

因此,讓我們將這些與您現有的代碼進行比較,都將其包裝在try / exceptif

def f_rev_ex(xs, s):
    xs.reverse()
    try:
        xs.remove(s)
    except ValueError:
        pass
    xs.reverse()

def f_rev_if(xs, s):
    if s in xs:
        xs.reverse()
        xs.remove(s)
        xs.reverse()

def f_for(xs, s):
    for i in range(len(xs)-1, -1, -1):
        if s == xs[i]:
            del xs[i]
            break

def f_enum(xs, s):
    for i, x in reversed(list(enumerate(xs))):
        if x == s:
            del xs[i]
            break

對於像你這樣小的列表,測試甚至不值得運行,所以我發明了自己的隨機數據(當然,在現實生活中你必須知道你的數據):

In [58]: xs = [random.choice(string.ascii_lowercase) for _ in range(10000)]
In [59]: %timeit y = x[:]; f_rev_ex(y, 'a')
10000 loops, best of 3: 34.7 µs per loop
In [60]: %timeit y = x[:]; f_rev_if(y, 'a')
10000 loops, best of 3: 35.1 µs per loop
In [61]: %timeit y = x[:]; f_for(y, 'a')
10000 loops, best of 3: 26.6 µs per loop
In [62]: %timeit y = x[:]; f_enum(y, 'a')
1000 loops, best of 3: 604 µs per loop

嗯,最后一個不是一個很好的主意……但另一個比我們開始的速度快 25%。 因此,我們在比實際數據大 4 個數量級的數據上節省了整整 9 微秒。 這取決於你是否值得使用可讀性較差、更容易搞砸的代碼。 (而且我不會在不復制的情況下向您展示我的基於enumerate的實現,因為我弄錯了。:P)

生成一個反向列表,保留原始索引並刪除您找到的第一個實例。

X = ['sf', 'cc', 'ch', 'sc', 'sh', 'ch']

print X

for i, e in reversed(list(enumerate(X))):
    if e == 'ch':
        del X[i]
        break

print X

如果它沒有找到字符串,它就不會改變列表。

首先,您可以使用if in語句檢查該項目是否在列表if in 然后您可以反轉列表並刪除元素。

if "ch" in X:
    X.reverse()
    X.remove("ch")
    X.reverse()

沒有 reverse() 並且類似於上面的一個答案:

def RightRemove(alist, x):
    for i in range(len(alist), 0, -1): # from end to begin
        if alist[i-1] == x: # element x exists
            alist.pop(i-1) # remove it
            break # return

還有一個答案...

def remove_last_occurrence(lst, element):
    '''
    Removes the last occurrence of a given element in a list (modifies list in-place).

    :return bool:
        True if the element was found and False otherwise.
    '''
    for i, s in enumerate(reversed(lst)):
        if s == element:
            del lst[len(lst) - 1 - i]
            return True
    return False

還有一個..

def remove_last_occurrence_one_liner(lst, element):
    """
    Removes the last occurrence of a given element in a list (modifies list in-place).
    Raises same exception than lst.index(element) if element can not be found.
    """
    del lst[len(lst) - lst[::-1].index(element) - 1]

但它並沒有擊敗abarnert的 for 循環

x = [random.choice(string.ascii_lowercase) for _ in range(10000)]
%timeit y = x[:]; f_rev_ex(y, 'a')
34.3 µs ± 219 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
%timeit y = x[:]; f_rev_if(y, 'a')
34.9 µs ± 195 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
%timeit y = x[:]; f_for(y, 'a')
26.9 µs ± 109 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
%timeit y = x[:]; f_enum(y, 'a')
699 µs ± 4.86 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
%timeit y = x[:]; remove_last_occurrence_one_liner(y, 'a')
49 µs ± 375 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

這是一個更簡單的函數,用於刪除元素在列表中的最后一次出現(如果找到):

def right_remove(l: list, element: Any) -> None:
    try:
        index = l[::-1].index(element)
    except ValueError:
        return
    
    l.pop(len(l) - index - 1)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM