Python:遍历列表并删除重复项(不使用Set())

3

我可以帮您翻译,这是一个列表:

s = ['cat','dog','cat','mouse','dog']

我希望能够遍历列表并删除重复项,但不使用set()函数!例如,它应该删除'cat'和位置s[2],但保留位置s[0]的'cat'。然后需要对'dog'执行相同的操作,即保留位置s[1]的'dog',但从位置s[4]删除'dog'。
因此,输出结果为:
s = ['cat','dog','mouse']

我尝试使用i和j作为列表中的索引位置,并检查位置i处的元素是否等于位置j处的元素。如果是,则会删除它并将j的值增加1,如果不是,则只会将j的值增加1而不删除它。在整个列表被迭代完毕后,它将增加i的值,然后再次检查整个列表,以获取新元素。如下:

i = 0
j = 1
for a in range(len(s)):
    for b in range(len(s)):
        if s[i] == s[j]:
            s.remove(s[j])
            j = j + 1
        else:
            j = j + 1
    i = i + 1

我在这里做错了什么?

使用"in"代替"=="。如果该值在数组中不存在,则将其存储在一个单独的数组中。 - almost a beginner
2
有没有什么原因需要直接更新列表而不是创建一个新的列表?l = []; for e in s: if e not in l: l.append(e) - AChampion
2
另一个问题是你正在遍历列表中的所有索引,直到列表的末尾。如果实际上存在重复项,当你到达那里时,列表将变短,并且你将收到一个IndexError错误。 - zondo
1
为什么不使用 set - luoluo
7个回答

7
问题出在“自动”循环中——当你修改正在迭代的内容时,使用它们要小心。这是正确的解决方案:
def remove_dup(a):
   i = 0
   while i < len(a):
      j = i + 1
      while j < len(a):
         if a[i] == a[j]:
            del a[j]
         else:
            j += 1
      i += 1

s = ['cat','dog','cat','mouse','dog']
remove_dup(s)
print(s)

Output: ['cat', 'dog', 'mouse']

这个解决方案是原地实现的,修改了原始数组而不是创建一个新的数组。 它也不使用任何额外的数据结构。


太感谢了伙计!一直在考虑使用while循环,却忽略了关于j的部分,在第一个while之后,而且list.remove()对我来说根本不起作用!谢谢! - Liam G
对我来说返回为None。列表 = ['94. / 日期:2022年2月16日','95. / 日期:2022年2月16日','96. / 日期:2022年2月16日','97. / 日期:2022年2月16日','98. / 日期:2022年2月16日','99. / 日期:2022年2月16日','100. / 日期:2022年2月16日','101. / 日期:2022年2月16日','102. / 日期:2022年2月18日','103. / 日期:2022年2月18日','103. / 日期:2022年2月18日','103. / 日期:2022年2月18日','103. / 日期:2022年2月18日','103. / 日期:2022年2月18日','103. / 日期:2022年2月18日'] - AnonymousUser
请确保您已经准确地复制了代码,并按照我所演示的方式使用它。是的,该函数返回None:请注意,该函数是一个原位修改列表的变异函数,如上所述。此外,我建议不要使用类似List这样的变量名,因为它与内置关键字list非常接近(如果您使用typing库,则是一个关键字)。 - Apollys supports Monica

6

您可以遍历列表并检查动物是否已经添加。

s = ['cat','dog','mouse','cat','horse','bird','dog','mouse']

sNew = []
for animal in s:
    if animal not in sNew:
        sNew.append(animal)

s = sNew

有没有“不在”这个操作的替代方法?我的任务目的是了解排序算法的工作原理,所以使用“不在”有点像作弊吧。 - Liam G

4

在迭代列表时不应更改列表,否则您可能会跳过元素或收到 IndexError。如果您确实无法使用 set,请使用collections.OrderedDict

>>> from collections import OrderedDict

>>> s = ['cat','dog','cat','mouse','dog']

>>> list(OrderedDict.fromkeys(s).keys())
['cat', 'dog', 'mouse']

不是楼主,但感谢你的回答。我正在努力理解你的代码。所以你可以使用OrderedDict,因为列表可以被视为只有键而没有值的字典?'fromkeys'是什么作用?谢谢。 - Bowen Liu
@BowenLiu fromkeys函数会根据指定的键创建一个字典。我使用有序字典是因为它能保持键的顺序,并且可以去除重复的键。对于字典的值,我们并不关心,但是没有内置的有序集合... - MSeifert
非常感谢您的解释和介绍这种新方法。我不知道您可以创建没有与每个键对应的值的字典。谢谢。 - Bowen Liu
请注意,在现代 Python(CPython/PyPy 3.6 以及任何 Python 3.7+)中,您可以直接使用 dict.fromkeys 来提高速度(dict 是按插入顺序排列的,除非您依赖于改变顺序的方法或对顺序敏感的比较,否则无需使用 OrderedDict)。在任何版本的 Python 中都不需要调用 .keys()dict 已经是其键的可迭代对象,因此 list(dict.fromkeys(s)) 就足够了。 - ShadowRanger

1

我不确定为什么你不想使用集合,但这里有一个替代方案。遍历原始列表,如果元素尚未在新列表中,则将其放入新列表中。例如:

l = []
s = ['dog', 'cat', 'cat', 'mouse', 'dog']

for i in range(len(s)):
    if s[i] not in l:
        l.append(s[i])

现在:

>>> s
['dog', 'cat', 'mouse']

2
更规范的做法是直接遍历列表s而不是索引。 - AChampion
非常正确。这样做可能更符合Python的风格。 - Remolten
有没有“不在”这个操作的替代方法?我的任务是要理解排序算法的工作原理,所以使用“不在”有点像作弊吧。 - Liam G

1
这是一个一行解决方案:

s = ['dog', 'cat', 'cat', 'mouse', 'dog']   

answer = [animal for idx, animal in enumerate(s) if a not in s[:idx]]

而且你会看到:

>>> answer
['cat', 'dog', 'mouse']

-1
s = ['cat','dog','cat','mouse','dog']
duplicates = []

for animal in s:
  if s.count(animal) > 1:
    if animal not in duplicates:
      duplicates.append(animal)
print(duplicates)

如果你解释一下你提供的代码是如何回答这个问题的,那么这将是一个更好的答案。 - pppery
这并没有提供其他答案没有涵盖的内容,实际上是错误的,因为它不保留任何东西,除非它是重复的(它确实将重复项减少为一个副本,但是消除非重复项是错误的)。例如,对于OP的情况,他们希望在结果中看到'mouse'作为最后一个元素,但是你却排除了它,因为它只出现一次。 - ShadowRanger

-2

这里只涉及类型转换,

s = ['cat','dog','cat','mouse','dog']

l = list(set(s)) 

print(l)

1
OP的问题明确排除了使用set;如果没有这个限制,这样做是可以的,但也会非常明显(问题中的限制是为什么其他人都没有发布它)。 - ShadowRanger

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接