Python 从列表中删除相邻的重复元素

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3460161/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-18 11:10:45  来源:igfitidea点击:

Remove adjacent duplicate elements from a list

python

提问by Vaibhav Bajpai

Google Python Class | List Exercise -

谷歌 Python 类 | 列表练习 -

Given a list of numbers, return a list where all adjacent == elements have been reduced to a single element, so [1, 2, 2, 3] returns [1, 2, 3]. You may create a new list or modify the passed in list.

给定一个数字列表,返回一个列表,其中所有相邻的 == 元素都已减少为单个元素,因此 [1, 2, 2, 3] 返回 [1, 2, 3]。您可以创建一个新列表或修改传入的列表。

My solution using a new list is -

我使用新列表的解决方案是 -

def remove_adjacent(nums):
  a = []
  for item in nums:
    if len(a):
      if a[-1] != item:
        a.append(item)
    else: a.append(item)        
  return a

The question even suggests that it could be done by modifying the passed in list. However, the python documentation warned against modifying elements while iterating a list using the for loop.

该问题甚至表明可以通过修改传入的列表来完成。但是,python 文档警告不要在使用 for 循环迭代列表时修改元素。

I am wondering what else can I try apart from iterating over the list, to get this done. I am not looking for the solution, but maybe a hint that can take me into a right direction.

我想知道除了迭代列表之外我还能尝试什么来完成这项工作。我不是在寻找解决方案,但也许是一个可以将我带入正确方向的提示。

UPDATE

更新

-updated the above code with suggested improvements.

- 使用建议的改进更新了上述代码。

-tried the following with a while loop using suggested hints -

- 使用建议的提示在 while 循环中尝试以下操作 -

def remove_adjacent(nums):
  i = 1
  while i < len(nums):    
    if nums[i] == nums[i-1]:
      nums.pop(i)
      i -= 1  
    i += 1
  return nums

采纳答案by Katriel

Use a generator to iterate over the elements of the list, and yielda new one only when it has changed.

使用生成器迭代列表的元素,yield只有当它发生变化时才使用一个新的。

itertools.groupbydoes exactly this.

itertools.groupby正是这样做的。

You can modify the passed-in list if you iterate over a copy:

如果您迭代副本,您可以修改传入的列表:

for elt in theList[ : ]:
    ...

回答by chryss

Well, katrielalex is right about itertools, but the OP seems to be rather more interested (or should be!) in learning to manipulate the basics of the built-in data structures. As for manipulating a list in place, it does need thought, but my recommendation would be to read through this section of the documentationand try a few list methods (hint: list.pop(), list.remove(), and learn everything about slices.)

好吧,katrielalex 是对的itertools,但是 OP 似乎对学习操作内置数据结构的基础知识更感兴趣(或者应该是!)。至于就地操作列表,确实需要考虑,但我的建议是通读文档的这一部分并尝试一些列表方法(提示:list.pop()、list.remove(),然后学习所有内容关于切片。)

The posted code could be simplified, by the way (you should however add handling of error conditions):

顺便说一下,可以简化发布的代码(但是,您应该添加对错误条件的处理):

def remove_adjacent(nums):
  a = nums[:1]
  for item in nums[1:]:
    if item != a[-1]:
      a.append(item)
  return a

回答by Joe Kington

@katrielalex's solution is more pythonic, but if you did need to modify the list in-place without making a copy, you could use a while loop and break when you catch an IndexError. e.g.

@katrielalex 的解决方案更加 pythonic,但是如果您确实需要在不进行复制的情况下就地修改列表,则可以使用 while 循环并在捕获 IndexError 时中断。例如

nums = [1,1,1,2,2,3,3,3,5,5,1,1,1]
def remove_adjacent(nums):
    """Removes adjacent items by modifying "nums" in-place. Returns None!"""
    i = 0
    while True:
        try:
            if nums[i] == nums[i+1]:
                # Letting you figure this part out, 
                # as it's a homework question
        except IndexError:
            break
print nums
remove_adjacent(nums)
print nums

Edit: pastebin of one way to do it here, in case you get stuck and want to know..

编辑:在这里做的一种方法的 pastebin,以防你被卡住并想知道..

回答by tc.

You canmodify a list you're iterating over if you use indices explicitly:

可以修改你迭代如果使用索引明确的清单:

def remove_adjacent(l):
  if len(l)<2:
    return l
  prev,i = l[0],1
  while i < len(l):
    if l[i] == prev:
      del l[i]
    else:
      prev = l[i]
      i += 1

It doesn't work with iterators because iterators don't "know" how to modify the index when you remove arbitrary elements, so it's easier to just forbid it. Some languages have iterators with functions to remove the "current item".

它不适用于迭代器,因为当您删除任意元素时,迭代器不“知道”如何修改索引,因此更容易禁止它。某些语言具有带有删除“当前项”功能的迭代器。

回答by Brian

Since you are in a Python class, I'm going to guess that you are new to the language. Thus, for you and any other beginners out there, I wrote a simple version of the code to help others get through the logic.

由于您在上 Python 课程,我猜您是该语言的新手。因此,对于您和那里的任何其他初学者,我编写了一个简单版本的代码来帮助其他人了解逻辑。

original= [1, 2, 2, 3]
newlist=[]

for item in original:
    if item in newlist:
        print "You don't need to add "+str(item)+" again."
    else:
        newlist.append(item)
        print "Added "+str(item)

print newlist

回答by Winston

You can use list comprehension. For example something like this should do the job:

您可以使用列表理解。例如这样的事情应该做的工作:

def remove_adjacent(L):
  return [elem for i, elem in enumerate(L) if i == 0 or L[i-1] != elem]

or:

或者:

def remove_adjacent(L):
  return [L[i] for i in xrange(len(L)) if i == 0 or L[i-1] != L[i]]

回答by Tony Veijalainen

Just to show one more way here is another single liner version without indexes:

只是为了展示另一种方式,这里是另一个没有索引的单行版本:

def remove_adjacent(nums):
     return [a for a,b in zip(nums, nums[1:]+[not nums[-1]]) if a != b]

The not part puts the last value to result as only a ends up to result.

not 部分将最后一个值放入 result ,因为只有 a 以 result 结束。

回答by Muhammad Alkarouri

As usual, I am just here to advertise the impressive recipesin the Python itertools documentation.

像往常一样,我只是在这里宣传Python itertools 文档中令人印象深刻的食谱

What you are looking for is the function unique_justseen:

您正在寻找的是功能unique_justseen

from itertools import imap, groupby
from operator import itemgetter

def unique_justseen(iterable, key=None):
    "List unique elements, preserving order. Remember only the element just seen."
    # unique_justseen('AAAABBBCCDAABBB') --> A B C D A B
    # unique_justseen('ABBCcAD', str.lower) --> A B C A D
    return imap(next, imap(itemgetter(1), groupby(iterable, key)))

list(unique_justseen([1,2,2,3])) # [1, 2, 3]

回答by John Machin

Here's the traditional way, deleting adjacent duplicates in situ, while traversing the list backwards:

这是传统的方法,原位删除相邻的重复项,同时向后遍历列表:

Python 1.5.2 (#0, Apr 13 1999, 10:51:12) [MSC 32 bit (Intel)] on win32
Copyright 1991-1995 Stichting Mathematisch Centrum, Amsterdam
>>> def dedupe_adjacent(alist):
...     for i in xrange(len(alist) - 1, 0, -1):
...         if alist[i] == alist[i-1]:
...             del alist[i]
...
>>> data = [1,2,2,3,2,2,4]; dedupe_adjacent(data); print data
[1, 2, 3, 2, 4]
>>> data = []; dedupe_adjacent(data); print data
[]
>>> data = [2]; dedupe_adjacent(data); print data
[2]
>>> data = [2,2]; dedupe_adjacent(data); print data
[2]
>>> data = [2,3]; dedupe_adjacent(data); print data
[2, 3]
>>> data = [2,2,2,2,2]; dedupe_adjacent(data); print data
[2]
>>>

Update:If you want a generator but (don't have itertools.groupbyor (you can type faster than you can read its docs and understand its default behaviour)), here's a six-liner that does the job:

更新:如果你想要一个生成器但是(没有itertools.groupby或(你可以比阅读它的文档和理解它的默认行为更快地输入),这里有一个六行代码可以完成这项工作:

Python 2.3.5 (#62, Feb  8 2005, 16:23:02) [MSC v.1200 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> def dedupe_adjacent(iterable):
...     prev = object()
...     for item in iterable:
...         if item != prev:
...             prev = item
...             yield item
...
>>> data = [1,2,2,3,2,2,4]; print list(dedupe_adjacent(data))
[1, 2, 3, 2, 4]
>>>

Update 2:Concerning the baroque itertools.groupby()and the minimalist object()...

更新 2:关于巴洛克风格itertools.groupby()和极简主义object()......

To get the dedupe_adjacent effect out of itertools.groupby(), you need to wrap a list comprehension around it to throw away the unwanted groupers:

要从 中获得 dedupe_adjacent 效果itertools.groupby(),您需要在其周围包裹一个列表推导式以丢弃不需要的石斑鱼:

>>> [k for k, g in itertools.groupby([1,2,2,3,2,2,4])]
[1, 2, 3, 2, 4]
>>>

... or muck about with itertools.imapand/or operators.itemgetter, as seen in another answer.

... 或乱用itertools.imap和/或operators.itemgetter,如另一个答案中所见。

Expected behaviour with objectinstances is that none of them compares equal to any other instance of any class, including objectitself. Consequently they are extremely useful as sentinels.

object实例的预期行为是,它们中没有一个与任何类的任何其他实例(包括object其自身)相比较。因此,它们作为哨兵非常有用。

>>> object() == object()
False

It's worth noting that the Python reference codefor itertools.groupbyuses object()as a sentinel:

值得一提的是,Python的引用代码itertools.groupby用途object()定为定点:

self.tgtkey = self.currkey = self.currvalue = object()

and that code does the right thing when you run it:

当您运行该代码时,该代码会做正确的事情:

>>> data = [object(), object()]
>>> data
[<object object at 0x00BBF098>, <object object at 0x00BBF050>]
>>> [k for k, g in groupby(data)]
[<object object at 0x00BBF098>, <object object at 0x00BBF050>]

Update 3:Remarks on forward-index in-situ operation

更新 3:关于前向索引原位操作的说明

The OP's revised code:

OP的修订代码:

def remove_adjacent(nums):
  i = 1
  while i < len(nums):    
    if nums[i] == nums[i-1]:
      nums.pop(i)
      i -= 1  
    i += 1
  return nums

is better written as:

最好写成:

def remove_adjacent(seq): # works on any sequence, not just on numbers
  i = 1
  n = len(seq)
  while i < n: # avoid calling len(seq) each time around
    if seq[i] == seq[i-1]:
      del seq[i]
      # value returned by seq.pop(i) is ignored; slower than del seq[i]
      n -= 1
    else:
      i += 1
  #### return seq #### don't do this
  # function acts in situ; should follow convention and return None

回答by Guest

def remove_adjacent(nums):

def remove_adjacent(nums):

newList=[]

for num in nums:

    if num not in newList:

        newList.append(num)

newList.sort()

return  newList