Проблема цикла при повторении через список и удалении повторяющихся элементов

Я хочу выполнить итерацию по списку и удалить элементы, которые считаются более одного раза, поэтому они не будут печататься повторно с помощью цикла for.

Однако некоторые элементы, появляющиеся только один раз в списке, похоже, тоже затронуты этим, и я не могу понять, почему.

Любой вход был бы весьма признателен.

Пример:

listy = [2,2,1,3,4,2,1,2,3,4,5] for i in listy: if listy.count(i)>1: print i, listy.count(i) while i in listy: listy.remove(i) else: print i, listy.count(i) 

Выходы:

  2 4 3 2 1 2 

таким образом игнорируя полностью 4 и 5.

  • Атрибут Mongoengine creation_time в документе
  • Определить размер кластера файловой системы в Python
  • Как создать оптимизатор в Tensorflow
  • Python - найти индекс элемента в списке списков
  • Как я могу выполнить запрос HEAD в библиотеке механизации?
  • Как подключить сокет после сломанного трубопровода?
  • Цвет линии 3D-параметрической кривой в matplotlib.pyplot на python
  • перечислить список словарей
  • 8 Solutions collect form web for “Проблема цикла при повторении через список и удалении повторяющихся элементов”

    Вы не должны изменять список во время итерации по нему. Это должно работать:

     listy = [2,2,1,3,4,2,1,2,3,4,5] found = set() for i in listy: if not i in found: print i, listy.count(i) found.add(i) 

    Результат:

     2 4 1 2 3 2 4 2 5 1 

    Причиной ваших проблем является то, что вы изменяете список во время его итерации по нему.

    Если вы не заботитесь о порядке, в котором элементы отображаются в выводе и не заботятся о счете, вы можете просто использовать набор:

     >>> listy = [2,2,1,3,4,2,1,2,3,4,5] >>> print set(listy) set([1, 2, 3, 4, 5]) 

    Если вы заботитесь о счете, используйте класс Counter из модуля collections в стандартной библиотеке:

     >>> import collections >>> collections.Counter(listy) Counter({2: 4, 1: 2, 3: 2, 4: 2, 5: 1}) >>> c = collections.Counter(listy) >>> for item in c.iteritems(): ... print "%i has a count of %i" % item ... 1 has a count of 2 2 has a count of 4 3 has a count of 2 4 has a count of 2 5 has a count of 1 

    Если вы заботитесь о заказе и счете, вам нужно создать второй список:

     >>> checked = [] >>> counts = [] >>> for item in listy: >>> if item not in checked: >>> checked.append(item) >>> counts.append(listy.count(item)) >>> print zip(checked, counts) ... [(2, 4), (1, 2), (3, 2), (4, 2), (5, 1)] 

    Это наименее эффективное решение, конечно.

    Если вы не хотите сохранять подсчеты позже, вам не нужен список counts :

     listy = [2,2,1,3,4,2,1,2,3,4,5] checked = set() for item in listy: # "continue early" looks better when there is lots of code for # handling the other case if item in checked: continue checked.add(item) print item, listy.count(item) 

    Не изменяйте список во время итерации по нему, это будет бесполезно для вас каждый раз:

     listy = [2,2,1,3,4,2,1,2,3,4,5] # * * * Get hit for i in listy: print i if listy.count(i) > 1: print i, listy.count(i), 'item and occurences' while i in listy: listy.remove(i) else: print i, listy.count(i) 
    1. Сначала вы удаляете четыре секунды. Два в начале, так что это ставит вас на первое 1 .
    2. Затем вы продвигаете его, когда вы получите следующий i из listy , поставив вас на первые 3 .
    3. Затем вы удаляете два 3 с. Первый находится прямо там, так что вы ставите перед собой первые 4 .
    4. Затем вы продвигаете один раз. 2 уже ушел, так что это ставит вас на второй 1 .
    5. Затем вы удаляете оба 1 s; это переводит вас вперед на два пробела. 2 и 3 ушли, так что это помещает вас в 5 .
    6. Вы продвигаете один, это отталкивает вас от конца списка, чтобы цикл закончился.

    Если вы хотите напечатать каждый элемент только один раз, вы можете использовать простой метод set или вы можете использовать рецепт itertools unique_everseen :

     def unique_everseen(iterable, key=None): "List unique elements, preserving order. Remember all elements ever seen." # unique_everseen('AAAABBBCCDAABBB') --> ABCD # unique_everseen('ABBCcAD', str.lower) --> ABCD seen = set() seen_add = seen.add if key is None: for element in ifilterfalse(seen.__contains__, iterable): seen_add(element) yield element else: for element in iterable: k = key(element) if k not in seen: seen_add(k) yield element 

    Что расширяет базовую версию set чтобы вы могли указать особый способ сравнения элементов.

    Если вы хотите узнать, какие элементы находятся только в списке один раз:

     listy2 = filter(lambda i: listy.count(i) == 1, listy) 

    listy2 теперь имеет все отдельные вхождения.

    Если вам не нравится лямбда, просто выполните:

     def getsingles(listy): def singles(i): return listy.count(i) == 1 return singles 

    тогда:

     listy2 = filter(getsingles(listy), listy) 

    Это делает специальную функцию, которая сообщит вам, какие элементы находятся в listy только один раз.

    Причина вашего поведения здесь, в примечании:

    http://docs.python.org/reference/compound_stmts.html#index-811

    Обновление 1

    Решение agf не подходит для повышения производительности: список фильтруется в соответствии со счетчиком каждого элемента. Счет выполняется для каждого элемента, то есть процесс подсчета, который состоит в том, чтобы пропустить весь список для подсчета, выполняется столько раз, сколько есть элементов в списке: это чрезмерное потребление времени, представьте, если ваш список имеет длину 1000

    Лучшее решение, я думаю, это использовать экземпляр Counter:

     import random from collections import Counter li = [ random.randint(0,20) for i in xrange(30)] c = Counter(li) print c print type(c) res = [ k for k in c if c[k]==1] print res 

    результат

     Counter({8: 5, 0: 3, 4: 3, 9: 3, 2: 2, 5: 2, 11: 2, 3: 1, 6: 1, 10: 1, 12: 1, 15: 1, 16: 1, 17: 1, 18: 1, 19: 1, 20: 1}) <class 'collections.Counter'> [3, 6, 10, 12, 15, 16, 17, 18, 19, 20] 

    Другим решением было бы добавить элементы чтения в набор, чтобы программа избегала делать счетчик для уже увиденного элемента.

    Обновление 2

    errrr …. мое решение глупо, вы не хотите выбирать элемент, появляющийся только один раз в списке ….

    Тогда следующий код является правильным, я думаю:

     import random from collections import Counter listy = [ random.randint(0,20) for i in xrange(30)] print 'listy==',listy print c = Counter(listy) print c print type(c) print slimmed_listy = [] for el in listy: if el in c: slimmed_listy.append(el) print 'element',el,' count ==',c[el] del c[el] print print 'slimmed_listy==',slimmed_listy 

    результат

     listy== [13, 10, 1, 1, 13, 11, 18, 15, 3, 15, 12, 11, 15, 18, 11, 10, 14, 10, 20, 3, 18, 9, 11, 2, 19, 15, 5, 14, 1, 1] Counter({1: 4, 11: 4, 15: 4, 10: 3, 18: 3, 3: 2, 13: 2, 14: 2, 2: 1, 5: 1, 9: 1, 12: 1, 19: 1, 20: 1}) <class 'collections.Counter'> element 13 count == 2 element 10 count == 3 element 1 count == 4 element 11 count == 4 element 18 count == 3 element 15 count == 4 element 3 count == 2 element 12 count == 1 element 14 count == 2 element 20 count == 1 element 9 count == 1 element 2 count == 1 element 19 count == 1 element 5 count == 1 slimmed_listy== [13, 10, 1, 11, 18, 15, 3, 12, 14, 20, 9, 2, 19, 5] 

    Если вам не нужен результат в порядке списка , код будет еще проще

    Обновление 3

    Если вы хотите только распечатать, я предлагаю:

     import random from collections import Counter listy = [ random.randint(0,20) for i in xrange(30)] print 'listy==',listy print def gener(li): c = Counter(li) for el in li: if el in c: yield el,c[el] del c[el] print '\n'.join('element %4s count %4s' % x for x in gener(listy)) 

    результат

     listy== [16, 2, 4, 9, 15, 19, 1, 1, 3, 5, 12, 15, 12, 3, 17, 13, 8, 11, 4, 6, 15, 1, 0, 1, 3, 3, 6, 5, 0, 8] element 16 count 1 element 2 count 1 element 4 count 2 element 9 count 1 element 15 count 3 element 19 count 1 element 1 count 4 element 3 count 4 element 5 count 2 element 12 count 2 element 17 count 1 element 13 count 1 element 8 count 2 element 11 count 1 element 6 count 2 element 0 count 2 

    Изменение списка во время перебора по нему – плохая идея на всех встречающихся мне языках. Мое предложение: не делай этого. Вот несколько лучших идей.

    Используйте set чтобы найти отдельные события

     source = [2,2,1,3,4,2,1,2,3,4,5] for s in set(source): print s 

    И вы получите следующее:

     >>> source = [2,2,1,3,4,2,1,2,3,4,5] >>> for s in set(source): ... print s ... 1 2 3 4 5 

    Если вы хотите подсчеты, используйте defaultdict

     from collections import defaultdict d = defaultdict(int) source = [2,2,1,3,4,2,1,2,3,4,5] for s in source: d[s] += 1 for k, v in d.iteritems(): print k, v 

    Вы получите следующее:

     >>> for k, v in d.iteritems(): ... print k, v ... 1 2 2 4 3 2 4 2 5 1 

    Если вы хотите, чтобы ваши результаты отсортированы, используйте sort и operator

     import operator for k, v in sorted(d.iteritems(), key=operator.itemgetter(1)): print k, v 

    Вы получите следующее:

     >>> import operator >>> for k, v in sorted(d.iteritems(), key=operator.itemgetter(1)): ... print k, v ... 5 1 1 2 3 2 4 2 2 4 

    Я не уверен, что рекомендуется перебирать список и удалять элементы одновременно. Если вы действительно хотите вывести все элементы и их количество, я бы сделал это следующим образом:

     listy = [2,2,1,3,4,2,1,2,3,4,5] listx = [] listc = [] for i in listy: if not i in listx: listx += [i] listc += [listy.count(i)] for x, c in zip(listx, listc): print x, c 

    Как сказал agf, изменение списка во время итерации приведет к проблемам. Вы можете решить свой код, используя while и pop :

     single_occurrences = [] while listy: i = listy.pop(0) count = listy.count(i)+1 if count > 1: print i, count while i in listy: listy.remove(i) else: print i, count single_occurrences.append(i) 

    Вывод:

     2 4 1 2 3 2 4 2 5 1 

    Один из способов сделать это – создать список результатов и проверить, находится ли в нем тестируемое значение:

     res=[] listy = [2,2,1,3,4,2,1,2,3,4,5] for i in listy: if listy.count(i)>1 and i not in res: res.append(i) for i in res: print i, listy.count(i) 

    Результат:

     2 4 1 2 3 2 4 2 
    Python - лучший язык программирования в мире.