Подтвердить что ты не робот

Удалите соседние повторяющиеся элементы из списка

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
4b9b3361

Ответ 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