Как удалить дубликаты из списка, добавив при этом количество дубликатов к соответствующим элементам списка? - PullRequest
0 голосов
/ 03 июля 2018

Мой вопрос такой же, как

за исключением того, что я также хотел бы, чтобы дубликаты в отражали количество дубликатов в самой строке элемента (в скобках).

Пример ввода:

myList = ["paper", "Plastic", "aluminum", "PAPer", "TIN", " paper", "glass", "tin", "PAPER", "Polypropylene Plastic"]

Единственно допустимый вывод:

myList = ["paper (3)", "Plastic", "aluminum", "TIN (2)", " paper", "glass", "Polypropylene Plastic"]

Примечания:

  • Обратите внимание, что если элемент ("Polypropylene Plastic") содержит другой элемент ("Plastic"), я все равно хотел бы сохранить оба элемента.

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

  • Оригинальный порядок списка должен быть сохранен.

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

Я ищу самый быстрый метод для достижения этой цели в Python 2.7.

Ответы [ 3 ]

0 голосов
/ 03 июля 2018

Вы также можете попробовать использовать объект collections.Counter() для отслеживания количества и использовать его для отслеживания того, какие слова были замечены, используя слова без регистра в качестве ссылки. Затем, когда вы закончите итерацию по списку ввода, обновите список результатов, чтобы количество слов в форме %s (%d), если количество больше 1.

Код:

from collections import Counter

words = ["paper", "Plastic", "aluminum", "PAPer", "TIN", " paper", "glass", "tin", "PAPER", "Polypropylene Plastic"]

counts = Counter()
result = []

for word in words:
    caseless = word.casefold()

    if caseless not in counts:
        result.append(word)

    counts[caseless] += 1

result = ['%s (%d)' % (w, counts[w.casefold()]) if counts[w.casefold()] > 1 
                                                else w for w in result]

print(result)

Выход:

['paper (3)', 'Plastic', 'aluminum', 'TIN (2)', ' paper', 'glass', 'Polypropylene Plastic'] 
0 голосов
/ 03 июля 2018

Вот версия, использующая один Counter, избегая использования другого set, как в решении @ RoadRunner, выдвигая ключи от Counter, когда мы их передаем. Это может быть немного медленнее, чем решение OrderedDict, если имеется много дубликатов, но будет использовать меньше памяти:

from collections import Counter

words = ["paper", "Plastic", "aluminum", "PAPer", "TIN", " paper", "glass", "tin", "PAPER", "Polypropylene Plastic"]

counter = Counter(w.lower() for w in words)

result = []
for word in words:
    key = word.lower()
    if key in counter:
        count = counter[key]
        if count == 1:
            result.append(word)
        else:
            result.append('{} ({})'.format(word, count))
        counter.pop(key)

Примечание Вы должны использовать casefold вместо lower для Python> = 3,3

0 голосов
/ 03 июля 2018

В исходном вопросе вы, вероятно, (я только что посмотрел на него) использовали set строк в сложенном регистре, чтобы посмотреть, есть ли у вас новая или повтор, создавая список новых по мере продвижения.

Вы можете заменить это на Counter вместо set. Но затем вам нужно построить список, а затем вернуться и отредактировать его с учетом.

Так что вместо этого замените оба set / Counter и список вывода на OrderedDict, в котором хранятся пары подсчета элементов для каждого сложенного в регистр элемента:

d = collections.OrderedDict()
for item in myList:
    caseless = item.lower()
    try:
        d[caseless][1] += 1
    except KeyError:
        d[caseless] = [item, 1]

… и затем передайте этот аргумент для создания списка вывода:

myList = []
for item, count in d.values():
    if count > 1:
        item = '{} ({})'.format(item, count)
    myList.append(item)

Вы можете сделать это более кратким (например, myList = ['{} ({})'.format(item, count) if count > 1 else item for item, count in d.values()), и это также сделает его немного быстрее с небольшим постоянным коэффициентом.

Вероятно, вы можете сбрить несколько наносекунд, используя % вместо format и, возможно, даже больше с %d вместо %s (хотя я думаю, что последняя часть больше не верна даже на 2,7) .

В зависимости от вашей платформы a[0] += 1 может быть быстрее или медленнее, чем a[1] += 1. Попробуйте оба способа, и если a[0] быстрее, используйте [count, item] пары вместо [item, count]. Если у вас тонна дупликов, вы можете рассмотреть класс с __slots__, который на самом деле может быть немного быстрее обновлять, но значительно медленнее создавать, чем список.

Кроме того, использование in теста или, возможно, сохранение d.__contains__ как локального, может быть быстрее, чем try - или это может быть медленнее, в зависимости от того, сколько повторений вы ожидаете иметь, поэтому попробуйте все три способа использования фактических данных вместо набора игрушечных данных.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...