Remove adjacent duplicate elements from a list
google 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 3 4 5 6 7 8 | 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 |
这个问题甚至暗示可以通过修改传入列表来完成。但是,python文档警告不要在使用for循环迭代列表时修改元素。
我想知道除了遍历列表之外,我还能尝试什么来完成这项工作。我不是在寻找解决方案,但可能是一个提示,可以把我带到一个正确的方向。
更新
-用建议的改进更新了上述代码。
-使用建议的提示尝试了下面的while循环-
1 2 3 4 5 6 7 8 | 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 |
号
这是传统的方法,在原位删除相邻的重复项,同时向后遍历列表:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 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] >>> |
更新:如果您想要一个生成器,但(没有
1 2 3 4 5 6 7 8 9 10 11 12 | 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] >>> |
。
更新2:关于巴洛克风格的
要使
1 2 3 | >>> [k for k, g in itertools.groupby([1,2,2,3,2,2,4])] [1, 2, 3, 2, 4] >>> |
号
…或者和
使用
1 2 | >>> object() == object() False |
号
值得注意的是,
1 | self.tgtkey = self.currkey = self.currvalue = object() |
号
当你运行代码时,它会做正确的事情:
1 2 3 4 5 | >>> 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>] |
号
更新3:关于前向指数原位操作的备注
操作规程修订代码:
1 2 3 4 5 6 7 8 | 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 |
号
最好写为:
1 2 3 4 5 6 7 8 9 10 11 12 | 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 |
号
使用生成器迭代列表中的元素,只有当列表发生更改时,才会使用
如果对副本进行迭代,则可以修改传入列表:
1 2 | for elt in theList[ : ]: ... |
这里还有一个没有索引的单行程序版本:
1 2 | def remove_adjacent(nums): return [a for a,b in zip(nums, nums[1:]+[not nums[-1]]) if a != b] |
Not部分将最后一个值放入result,因为只有a的结果。
像往常一样,我来这里只是在python itertools文档中宣传令人印象深刻的食谱。
您要查找的是函数
1 2 3 4 5 6 7 8 9 10 | 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] |
。
好吧,凯特里·亚历克斯对埃多克斯的看法是正确的,但是手术似乎更感兴趣(或者应该是!)学习操作内置数据结构的基础知识。至于就地操作一个列表,它确实需要考虑,但是我的建议是阅读文档的这一部分,并尝试一些列表方法(提示:list.pop(),list.remove(),并了解有关切片的所有信息)。
通过这种方式,可以简化已发布的代码(但是您应该添加对错误条件的处理):
1 2 3 4 5 6 | def remove_adjacent(nums): a = nums[:1] for item in nums[1:]: if item != a[-1]: a.append(item) return a |
号
试试这个:
1 2 3 4 5 6 7 8 9 | def remove_adjacent(nums): result = [] if len(nums) > 0: result = [nums[0]] for i in range(len(nums)-1): if nums[i] != nums[i+1]: result.append(nums[i+1]) return result |
。
1 | reduce(lambda x, y: x + [y] if x[-1] != y else x, seq[1:], seq[0:1]) |
号
例如
1 2 3 | >>> seq = [[1,1], [2,2], [3,3], [3,3], [2,2], [2,2], [1,1]] >>> print reduce(lambda x, y: x + [y] if x[-1] != y else x, seq[1:], seq[0:1]) [[1, 1], [2, 2], [3, 3], [2, 2], [1, 1]] |
当来自函数语言的时候,使用
来自Google的非常优雅的解决方案(来源:https://developers.google.com/edu/python/exercises/basic):
1 2 3 4 5 6 | def remove_adjacent(nums): result = [] for num in nums: if len(result) == 0 or num != result[-1]: result.append(num) return result |
号
你可以使用列表理解。例如,像这样的事情应该可以做到:
1 2 | def remove_adjacent(L): return [elem for i, elem in enumerate(L) if i == 0 or L[i-1] != elem] |
。
或:
1 2 | def remove_adjacent(L): return [L[i] for i in xrange(len(L)) if i == 0 or L[i-1] != L[i]] |
。
1 2 3 4 5 6 7 8 9 10 11 12 13 | def removeDupAdj2(a): b=[] for i in reversed(range(1,len(a))): if(a[i-1] == a[i]): del(a[i]) #print(a) return a a = [int(i) for i in '1 2 3 3 4 4 3 5 4 4 6 6 6 7 8 8 8 9 1 1 0 0'.split(' ')] a res = removeDupAdj2(a) res |
号
另一种方法。欢迎评论。
1 2 3 4 5 6 7 8 9 10 11 | def remove_adjacent(nums): '''modifies the list passed in''' l, r = 0, 1 while r < len(nums): if nums[l] == nums[r]: r += 1 else: l += 1 nums[l] = nums[r] r += 1 del nums[l+1:] |
号
看到谷歌写的代码是一个令人谦卑的LOL。这就是我想到的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | def remove_adjacent(nums): rmvelement = [] checkedIndex = [] for num in nums: if nums.index(num) not in checkedIndex: index = nums.index(num) checkedIndex.append(index) skip = False else: skip = True if skip == False: for x in nums[index+1:]: if x == num: rmvelement.append(x) else: break [nums.remove(_) for _ in rmvelement] return nums |
号
这应该适用于一个透明的(尽管是迂回的)解决方案:
1 2 3 4 5 6 7 8 9 10 | def remove_adjacent(nums): numstail = [i for i in range(0,len(nums))] nums = nums + numstail for i in nums: if nums[i] == nums[i-1]: del nums[i] return nums[:-len(numstail)] |
逻辑如下:
- 创建一个与原始数字列表长度相等的尾部列表,并将其附加到原始列表的末尾。
- 运行"for循环",检查nums的给定元素是否与前一个元素相同。如果是,删除它。
- 返回新的nums列表,并进行必要的删除,从列表末尾最多返回
len(numtails) 个索引位置。
(
如果显式使用索引,则可以修改正在迭代的列表:
1 2 3 4 5 6 7 8 9 10 | 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 |
。
它不适用于迭代器,因为迭代器不知道在删除任意元素时如何修改索引,所以更容易禁止它。有些语言有迭代器,它具有删除"当前项"的函数。
@KatrielAlex的解决方案更像是Python,但是如果您确实需要在不复制的情况下就地修改列表,则可以使用while循环并在捕获indexError时中断。例如
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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 |
编辑:在这里粘贴一种方法,以防你卡住并想知道。
def移除相邻(nums):
1 2 3 4 5 6 7 8 9 10 11 | newList=[] for num in nums: if num not in newList: newList.append(num) newList.sort() return newList |
号
既然您在一个Python类中,我猜您对该语言还是个新手。因此,对于您和其他初学者来说,我编写了一个简单的代码版本来帮助其他人理解逻辑。
1 2 3 4 5 6 7 8 9 10 11 | 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 |