如何在 Python 列表中查找某项的最后一次出现

2025-02-20 09:24:00
admin
原创
69
摘要:问题描述:假设我有这个列表:li = ["a", "b", "a", "c", "x", "d", "a", "6"] 据帮助显示,没有内置函数返回字...

问题描述:

假设我有这个列表:

li = ["a", "b", "a", "c", "x", "d", "a", "6"]

据帮助显示,没有内置函数返回字符串的最后一次出现(如 的反向index)。所以基本上,我如何"a"在给定列表中找到 的最后一次出现?


解决方案 1:

如果您实际上只使用单个字母(如示例中所示),那么str.rindex将非常方便。这将引发ValueError与不存在此类项相同的错误类list.index。演示:

>>> li = ["a", "b", "a", "c", "x", "d", "a", "6"]
>>> ''.join(li).rindex('a')
6

对于更一般的情况,你可以list.index在反向列表中使用:

>>> len(li) - 1 - li[::-1].index('a')
6

此处的切片会创建整个列表的副本。这对于短列表来说没问题,但对于非常长的情况li,使用反向迭代并避免复制可能会更有效:

def list_rindex(li, x):
    for i in reversed(range(len(li))):
        if li[i] == x:
            return i
    raise ValueError("{} is not in list".format(x))

单行版本:

next(i for i in reversed(range(len(li))) if li[i] == 'a')

解决方案 2:

类似于 Ignacio 的一句话,但更简单/更清晰一点,是

max(loc for loc, val in enumerate(li) if val == 'a')

对我来说,它看起来非常清晰且符合 Python 风格:您正在寻找包含匹配值的最高索引。无需 nexts、lambdas、reverseds 或 itertools。

解决方案 3:

许多其他解决方案都需要迭代整个列表。这个不需要。

def find_last(lst, elm):
  gen = (len(lst) - 1 - i for i, v in enumerate(reversed(lst)) if v == elm)
  return next(gen, None)

编辑:事后看来,这似乎是不必要的魔法。我会做这样的事情:

def find_last(lst, sought_elt):
    for r_idx, elt in enumerate(reversed(lst)):
        if elt == sought_elt:
            return len(lst) - 1 - r_idx

解决方案 4:

>>> (x for x in reversed(list(enumerate(li))) if x[1] == 'a').next()[0]
6

>>> len(li) - (x for x in enumerate(li[::-1]) if x[1] == 'a').next()[0] - 1
6

解决方案 5:

last_occurence=len(yourlist)-yourlist[::-1].index(element)-1

就这么简单。无需导入或创建函数。

解决方案 6:

我喜欢wim和Ignacio 的答案。不过,我认为itertools提供了一个更易读的替代方案,尽管 lambda 如此。(对于 Python 3;对于 Python 2,使用xrange而不是range)。

>>> from itertools import dropwhile
>>> l = list('apples')
>>> l.index('p')
1
>>> next(dropwhile(lambda x: l[x] != 'p', reversed(range(len(l)))))
2

StopIteration如果未找到该项目,这将引发异常;您可以捕获该异常并引发ValueError异常,以使其行为就像一样index

定义为函数,避免使用lambda快捷方式:

def rindex(lst, item):
    def index_ne(x):
        return lst[x] != item
    try:
        return next(dropwhile(index_ne, reversed(range(len(lst)))))
    except StopIteration:
        raise ValueError("rindex(lst, item): item not in list")

它也适用于非字符。已测试:

>>> rindex(['apples', 'oranges', 'bananas', 'apples'], 'apples')
3

解决方案 7:

dict

您可以使用这样一个事实:字典键是唯一的,当使用元组构建字典时,只会使用特定键的最后一个值分配。如其他答案所述,这对于小列表来说很好,但它会为所有唯一值创建一个字典,对于大列表来说可能效率不高。

dict(map(reversed, enumerate(li)))["a"]

6

解决方案 8:

我来这里是希望找到已经编写了最有效版本的人list.rindex,该版本提供了完整的接口list.index(包括可选startstop参数)。我没有在这个问题的答案、这里、这里或这里找到它。所以我自己把它们放在一起……利用了这个问题和其他问题的其他答案的建议。

def rindex(seq, value, start=None, stop=None):
  """L.rindex(value, [start, [stop]]) -> integer -- return last index of value.
  Raises ValueError if the value is not present."""
  start, stop, _ = slice(start, stop).indices(len(seq))
  if stop == 0:
    # start = 0
    raise ValueError('{!r} is not in list'.format(value))
  else:
    stop -= 1
    start = None if start == 0 else start - 1
  return stop - seq[stop:start:-1].index(value)

在其他几个答案中建议使用 的技术len(seq) - 1 - next(i for i,v in enumerate(reversed(seq)) if v == value)可以更节省空间:它不需要创建完整列表的反向副本。但在我的(随意的)测试中,它大约慢了 50%。

解决方案 9:

这里有很多答案,所以我认为比较不同的方法会很有趣。我还想出了一个这里未列出的非常好的解决方案,并创建了一个PyPI 包。

让我们直接进入正题,下面是我用于基准测试的代码:

from timeit import timeit
from itertools import dropwhile
from operator import indexOf

from rindex import rindex


def rindex1(li, value):
    return len(li) - 1 - li[::-1].index(value)


def rindex2(li, value):
    for i in reversed(range(len(li))):
        if li[i] == value:
            return i
    raise ValueError("{} is not in list".format(value))


def rindex3(li, value):
    return max(loc for loc, val in enumerate(li) if val == value)


def rindex4(li, value):
    for r_idx, elt in enumerate(reversed(li)):
        if elt == value:
            return len(li) - 1 - r_idx


def rindex5(li, value):
    return next(dropwhile(lambda x: li[x] != value, reversed(range(len(li)))))


def rindex6(li, value):
    return dict(map(reversed, enumerate(li)))[value]


def rindex7(seq, value, start=None, stop=None):
    """L.rindex(value, [start, [stop]]) -> integer -- return last index of value.
    Raises ValueError if the value is not present."""
    start, stop, _ = slice(start, stop).indices(len(seq))
    if stop == 0:
        # start = 0
        raise ValueError("{!r} is not in list".format(value))
    else:
        stop -= 1
        start = None if start == 0 else start - 1
    return stop - seq[stop:start:-1].index(value)


# Thanks Kelly Bundy for mentioning this in the comments
# https://stackoverflow.com/a/63834895
def rindex8(li, value):
    return len(li) - 1 - indexOf(reversed(li), value)


def my_rindex(li, value, start=0, stop=None, /):
    size = len(li)
    li.reverse()
    try:
        return (
            size - 1 - li.index(value, 0 if stop is None else size - stop, size - start)
        )
    finally:
        li.reverse()


FUNCTIONS = (rindex, rindex1, rindex2, rindex3, rindex4, rindex5, rindex6, rindex7, rindex8, my_rindex)
FUNCTIONS_WITH_BOUNDARIES = (rindex7, my_rindex, rindex)


def test_correctness():
    li = [0, 1, 0, 2]
    for f in FUNCTIONS:
        assert f(li, 0) == 2
    for f in FUNCTIONS_WITH_BOUNDARIES:
        assert f(li, 0, 0, 1) == 0


def test_speed():
    small = list(range(10))
    big = list(range(1_000_000))
    tests = (
        ("small best", small, len(small) - 1, 1_000_000),
        ("big best", big, len(big) - 1, 100),
        ("small middle", small, len(small) // 2, 1_000_000),
        ("big middle", big, len(big) // 2, 100),
        ("small worst", small, 0, 1_000_000),
        ("big worst", big, 0, 100),
    )
    for name, li, value, repeats in tests:
        print(format(f" {name} ", "=^22"))
        for f in (list.index,) + FUNCTIONS:
            print(
                f.__name__.ljust(10),
                ":",
                format(
                    timeit(
                        "f(li, value)",
                        globals={
                            "f": f,
                            "li": li,
                            "value": value
                            if f is not list.index
                            else len(li) - 1 - value,
                        },
                        number=repeats,
                    ),
                    "9f",
                ),
            )


if __name__ == "__main__":
    test_correctness()
    test_speed()

结果如下:

===== small best =====
index      :  0.124899
rindex     :  0.168111
rindex1    :  0.711771
rindex2    :  1.042878
rindex3    :  3.031604
rindex4    :  1.018811
rindex5    :  2.309284
rindex6    :  8.428993
rindex7    :  1.536563
rindex8    :  0.714625
my_rindex  :  0.751303
====== big best ======
index      :  0.000020
rindex     :  0.000030
rindex1    :  2.520536
rindex2    :  0.000187
rindex3    : 12.694951
rindex4    :  0.000135
rindex5    :  0.000276
rindex6    : 82.994252
rindex7    :  2.820221
rindex8    :  0.000113
my_rindex  :  0.619653
==== small middle ====
index      :  0.264816
rindex     :  0.325221
rindex1    :  0.875725
rindex2    :  1.440296
rindex3    :  3.100110
rindex4    :  1.424884
rindex5    :  3.330751
rindex6    :  8.430511
rindex7    :  1.686265
rindex8    :  0.888111
my_rindex  :  0.888112
===== big middle =====
index      :  1.813371
rindex     :  1.747949
rindex1    :  4.465189
rindex2    :  5.886711
rindex3    : 12.696632
rindex4    :  6.856758
rindex5    : 13.855785
rindex6    : 84.861101
rindex7    :  4.457431
rindex8    :  2.044351
my_rindex  :  2.299312
==== small worst =====
index      :  0.452868
rindex     :  0.456035
rindex1    :  1.016495
rindex2    :  1.846306
rindex3    :  3.038056
rindex4    :  1.901025
rindex5    :  4.539291
rindex6    :  8.428476
rindex7    :  1.841232
rindex8    :  1.061607
my_rindex  :  1.054495
===== big worst ======
index      :  3.620210
rindex     :  3.083546
rindex1    :  5.824754
rindex2    : 11.764573
rindex3    : 12.696748
rindex4    : 13.481179
rindex5    : 27.432877
rindex6    : 82.762718
rindex7    :  5.880688
rindex8    :  3.685528
my_rindex  :  3.725783

此处,“最佳”情况意味着搜索的值位于最右边,“中间” - 位于中间,“最差” - 位于最左边。从技术上讲,最差情况是搜索的值不存在,但处理异常可能会扰乱测量结果。

rindex我的rindex软件包中的功能更出色(在某些情况下甚至优于内置功能index),因为它是用 Cython 编写的。如果您需要最佳性能,请使用它。

纯Python函数比较:

我们还可以将rindex3rindex5和排除rindex6在竞争之外:它们不具备优势。

rindex2rindex4几乎相同,但 4 在“好”情况下稍快一些,而在“坏”情况下则相反。它们也被排除在外,因为rindex8在所有情况下都更快。

rindex1对于小列表来说可能是最好的(但是,速度rindex8只是my_rindex稍微慢一点)。

对于其他情况rindex8是最快的(甚至可以与内置的index“最坏”情况相媲美)。

my_rindex几乎一样快,但在“最大最佳”情况下速度会急剧下降。不过,它支持边界。

然而,如果你使用边界,有效地将大列表缩小到小列表,rindex7就会成为你的朋友。

解决方案 10:

使用一个简单的循环:

def reversed_index(items, value):
    for pos, curr in enumerate(reversed(items)):
        if curr == value:
            return len(items) - pos - 1
    raise ValueError("{0!r} is not in list".format(value))

解决方案 11:

喜欢@alcalde 的解决方案,但是遇到ValueError:如果没有元素符合条件,则max() arg 是一个空序列。

为了避免错误设置default=None

max((loc for loc, val in enumerate(li) if val == 'a'), default=None)

解决方案 12:

lastIndexOf = lambda array, item: len(array) - (array[::-1].index(item)) - 1

解决方案 13:

如果列表很小,您可以计算所有索引并返回最大的索引:

index = max(i for i, x in enumerate(elements) if x == 'foo')

解决方案 14:

def rindex(lst, val):
  try:
    return next(
      len(lst) - n
      for n, v in enumerate(reversed(lst), start=1)
      if v == val
    )
  except StopIteration:
    raise ValueError(f'{val} is not in list')

解决方案 15:

值 = [1,2,2,2,2,2,4,5].

如果你需要找到最后一次出现的 2

last_occurence = (len(val) -1) - list(reversed(val)).index(2)

解决方案 16:

enumerate下面是使用列表推导获取最后一个索引的一行小代码:

li = ["a", "b", "a", "c", "x", "d", "a", "6"]
[l[0] for l in enumerate(li) if l[1] == "a"][-1]

解决方案 17:

最 Python 化、最有效的方式,也适用于非字符串列表:

len(li) - next((i for i, e in enumerate(reversed(li)) if e == "a"), len(li) + 1)

如果未找到,则表达式的计算结果为-1

无需构建新列表,100% 依赖迭代器。

相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   3911  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   2721  
  本文介绍了以下10款项目管理软件工具:禅道项目管理软件、Freshdesk、ClickUp、nTask、Hubstaff、Plutio、Productive、Targa、Bonsai、Wrike。在当今快速变化的商业环境中,项目管理已成为企业成功的关键因素之一。然而,许多企业在项目管理过程中面临着诸多痛点,如任务分配不...
项目管理系统   61  
  本文介绍了以下10款项目管理软件工具:禅道项目管理软件、Monday、TeamGantt、Filestage、Chanty、Visor、Smartsheet、Productive、Quire、Planview。在当今快速变化的商业环境中,项目管理已成为企业成功的关键因素之一。然而,许多项目经理和团队在管理复杂项目时,常...
开源项目管理工具   61  
  本文介绍了以下10款项目管理软件工具:禅道项目管理软件、Smartsheet、GanttPRO、Backlog、Visor、ResourceGuru、Productive、Xebrio、Hive、Quire。在当今快节奏的商业环境中,项目管理已成为企业成功的关键因素之一。然而,许多企业在选择项目管理工具时常常面临困惑:...
项目管理系统   54  
热门文章
项目管理软件有哪些?
曾咪二维码

扫码咨询,免费领取项目管理大礼包!

云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用