2016-01-12 4 views
2

Я искал какое-то время, но я не могу найти решение моей проблемы. Я все еще новичок в Python, поэтому я когда-нибудь борется с очевидными вещами ... Спасибо заранее за ваши советы!Python: remove() не работает

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

Я попытался использовать метод remove(), так как в списке может быть только одно вхождение каждого имени, но оно не работает. Вот мой код:

rawSelection = [u'crapacruk_high', u'doubidou_high', u'blahbli_high', u'crapacruk_SREF_high', u'doubidou_SREF_high', u'blahbli_SREF_high'] 
# objects with '_SREF_' in their names are the duplicated ones 
for obj in rawSelection: 
    if '_SREF_' in str(obj): 
     rawName = str(obj).split('_') 
     rootName = rawName [0] 
     defName = rootName + '_' + '_'.join(rawName[2:]) 
     if defName in rawSelection: 
      rawSelection.remove (obj) 
# Always returns: 
# [u'crapacruk_high', u'doubidou_high', u'blahbli_high', u'doubidou_SREF_high'] 
# Instead of: 
# [u'crapacruk_high', u'doubidou_high', u'blahbli_high'] 

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

+5

Не перебирайте список во время его изменения. Это никогда не сработает. –

+0

Вам будет намного лучше просто создать объект 'set' из вашего объекта' list'. Он удалит дубликаты: 'newobj = set (mylist)' EDIT: nevermind. Не понял, что строки не идентичны. –

+0

@Morgan Thrapp Есть ли у вас альтернатива? Может быть, с генератором? Спасибо – UKDP

ответ

1

Вы можете повернуть цикл for от for obj in rawSelection: до for obj in list(rawSelection):. Это должно исправить вашу проблему, поскольку она выполняет итерацию над копией списка. Как вы это делаете, вы изменяете список во время итерации по нему, что приводит к проблемам.

rawSelection = [u'crapacruk_high', u'doubidou_high', u'blahbli_high', u'crapacruk_SREF_high', u'doubidou_SREF_high', u'blahbli_SREF_high'] 

for obj in list(rawSelection): 
    if '_SREF_' in str(obj): 
     rawName = str(obj).split('_') 
     rootName = rawName [0] 
     defName = rootName + '_' + '_'.join(rawName[2:]) 
     if defName in rawSelection: 
      rawSelection.remove (obj) 

print(rawSelection) 
+0

Я не знал этого трюка, спасибо, он отлично работает! – UKDP

0

Это будет делать то, что вы хотите (обратите внимание, что это не имеет значения, в каком порядке элементы появляются в):

rawSelection = list({i.replace('_SREF', '') for i in rawSelection}) 

Это работает перебирая исходного списка и удаление '_SREF' подстроку от каждого элемента. Затем каждый отредактированный строковый объект добавляется к значению set (вот что означают скобки {}: создается новый объект set). Затем объект set возвращается обратно в объект list.

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

EDIT: как указано в комментариях @PeterDeGlopper, это не работает для ограничения того, что элемент _SREF_ удаляется только в случае появления оригинала. Для этого мы сделаем следующее:

no_SREF_Set = {i for i in rawSelection if '_SREF_' not in i} 
rawSelection = list({i.replace('_SREF', '') if i.replace('_SREF', '') in no_SREF_Set else i for i in rawSelection}) 

Вы можете совместить это в одном лайнере, но это немного долго, на мой вкус:

rawSelection = list({i.replace('_SREF', '') if i.replace('_SREF', '') in {i for i in rawSelection if '_SREF_' not in i} else i for i in rawSelection}) 

Это работает путем создания set из элементы, которые не имеют '_SREF_', а затем создают новый list (аналогично вышеописанному), который заменяет только '_SREF', если в no_SREF_Set номер версии '_SREF_'.

+0

На самом деле, нет, это не позволяет ограничить, что «дублированный объект должен быть удален из списка, только если исходный в нем тоже». –

+1

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

+0

@PeterDeGlopper Отредактирован ответ, чтобы исправить проблему, указанную в первом комментарии. Правда, порядок не сохранился. –

3

Проблема в том, что вы мутируете тот же список, что и итерации.

Когда вы удаляете u'crapacruk_SREF_high' из списка, все после него сдвигается влево (это делается на уровне исходного кода C), поэтому значение obj теперь u'doubidou_SREF_high'. Затем заканчивается конец цикла for, и obj становится следующим элементом в списке, u'blahbli_SREF_high'.

Чтобы исправить это, вы можете скопировать список снова и получить

for obj in rawSelection[:]: 
    ... 
0

Разбейте проблему вверх в подзадач

def get_orig_name(name): 
    if '_SREF_' in name: 
     return '_'.join(name.split('_SREF_')) 
    else: 
     return name 

Тогда просто построить новый список, без дубликатов

rawSelection = [u'crapacruk_high', 
       u'doubidou_high', 
       u'blahbli_high', 
       u'crapacruk_SREF_high', 
       u'doubidou_SREF_high', 
       u'blahbli_SREF_high'] 

uniqueList = [ n for n in rawSelection if ('_SREF_' not in n) or  
              (get_orig_name(n) not in rawSelection) ] 

print uniqueList 
0

Вы можете использовать filter, чтобы получить довольно чистое решение.

def non_duplicate(s): 
    return not('_SREF_' in s and s.replace('_SREF', '') in raw_selection) 

filtered_selection = filter(non_duplicate, raw_selection) 
Смежные вопросы