Удалите соседние повторяющиеся элементы из списка
Google Python Class | Список упражнений -
Учитывая список чисел, верните список, где все смежные элементы == были сведены к одному элементу, поэтому [1, 2, 2, 3] возвращает [1, 2, 3]. Вы можете создать новый список или изменить прошедший список.
Мое решение с использованием нового списка -
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, используя предложенные подсказки -
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
Используйте генератор для итерации по элементам списка, а yield
- новый, только когда он изменился.
itertools.groupby
делает именно это.
Вы можете изменить список отправленных, если вы перебираете копию:
for elt in theList[ : ]:
...
Ответ 2
Здесь традиционный способ удаления смежных дубликатов на месте при перемещении списка назад:
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]
>>>
Обновление: Если вы хотите генератор, но (не имеете 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]
>>>
Обновление 2: Что касается барокко itertools.groupby()
и минималиста object()
...
Чтобы получить эффект dedupe_adjacent из itertools.groupby()
, вам необходимо обернуть вокруг него понимание списка, чтобы выбросить нежелательные группы:
>>> [k for k, g in itertools.groupby([1,2,2,3,2,2,4])]
[1, 2, 3, 2, 4]
>>>
... или muck about с itertools.imap
и/или operators.itemgetter
, как видно из другого ответа.
Ожидаемое поведение с экземплярами object
заключается в том, что ни один из них не сравнится с каким-либо другим экземпляром любого класса, включая object
. Следовательно, они чрезвычайно полезны в качестве стражей.
>>> object() == object()
False
Стоит отметить, что код ссылки Python для itertools.groupby
использует object()
как часовое:
self.tgtkey = self.currkey = self.currvalue = object()
и этот код работает правильно, когда вы его запускаете:
>>> 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: Замечания о работе с индексом forward-index in-situ
Пересмотренный код 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
лучше писать как:
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
Ответ 3
Просто показать еще один способ - это еще одна версия линейки без индексов:
def remove_adjacent(nums):
return [a for a,b in zip(nums, nums[1:]+[not nums[-1]]) if a != b]
Не часть возвращает последнее значение, поскольку результат заканчивается.
Ответ 4
Как обычно, я просто здесь, чтобы рекламировать впечатляющий recipes в документации Python itertools.
То, что вы ищете, это функция 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]
Ответ 5
Хорошо, katrielalex прав около itertools
, но OP, похоже, более заинтересован (или должен быть!) в обучении манипулированию основами встроенных структур данных. Что касается манипулирования списком на месте, ему действительно нужно подумать, но моя рекомендация состояла бы в том, чтобы прочитать этот раздел документации и попробовать несколько (подсказка: list.pop(), list.remove() и узнать все о срезах.)
Продвинутый код может быть упрощен, кстати (вы должны, однако, добавить обработку ошибок):
def remove_adjacent(nums):
a = nums[:1]
for item in nums[1:]:
if item != a[-1]:
a.append(item)
return a
Ответ 6
Вы можете использовать понимание списка. Например, что-то вроде этого должно выполнить эту задачу:
def remove_adjacent(L):
return [elem for i, elem in enumerate(L) if i == 0 or L[i-1] != elem]
или
def remove_adjacent(L):
return [L[i] for i in xrange(len(L)) if i == 0 or L[i-1] != L[i]]
Ответ 7
Чрезвычайно элегантное решение от Google (источник здесь: https://developers.google.com/edu/python/exercises/basic):
def remove_adjacent(nums):
result = []
for num in nums:
if len(result) == 0 or num != result[-1]:
result.append(num)
return result
Ответ 8
itertools.groupby
превосходит, но есть и
reduce(lambda x, y: x + [y] if x[-1] != y else x, seq[1:], seq[0:1])
например.
>>> 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]]
При переходе с функциональных языков, где это происходит с помощью fold
, использование reduce
часто кажется естественным.
Ответ 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
Ответ 10
Решение @katrielalex более pythonic, но если вам нужно было изменить список на месте без создания копии, вы можете использовать цикл while и break, когда вы поймаете 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 a homework question
except IndexError:
break
print nums
remove_adjacent(nums)
print nums
Изменить: pastebin одного из способов сделать это здесь, если вы застряли и хотите узнать..
Ответ 11
Вы можете изменить список, который вы выполняете, если вы явно используете индексы:
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
Он не работает с итераторами, потому что итераторы не "знают", как изменять индекс при удалении произвольных элементов, поэтому проще просто запретить его. На некоторых языках есть итераторы с функциями для удаления "текущего элемента".
Ответ 12
def remove_adjacent (nums):
newList=[]
for num in nums:
if num not in newList:
newList.append(num)
newList.sort()
return newList
Ответ 13
Другой подход. Комментарии приветствуются.
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:]
Ответ 14
Увидев код, написанный Google, это унизительный LOL. Вот что я придумал:
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
Ответ 15
Это должно работать для прозрачного (хотя и кругового) решения:
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-loop', который проверяет, является ли данный элемент nums таким же, как предыдущий элемент. Если это так, удалите его.
- Возвращает новый список nums с необходимыми удалениями, вплоть до
len(numtails)
позиции индекса в конце списка.
(numstail
определяется, чтобы избежать превышения индексов вне диапазона для любого списка длины)
Ответ 16
Поскольку вы находитесь в классе 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