Фильтрация списка на основе суффикса и предотвращение дублирования

Мне было интересно, есть ли лучший способ решить эту проблему.

У меня есть такой список:

imp_list=["aa","daa","ab","as","aem",
      "aum","aw","aa","acm","at",
      "ar","aa_imp","daa_imp","ab_imp",
      "as_imp"]

Я хочу выбрать все строки с _imp суффикс, плюс все строки, у которых нет _imp партнер.

Это потому что aa_imp это просто модифицированная версия aa, и для меня, в широком смысле, это реплика. Итак, я создал эту функцию:

def imputed_handler(my_list): 
  imp=[x for x in my_list if "imp" in x]

  cleaned_imp=set(map(lambda x: x.replace("_imp",""),imp))

  not_imp=[x for x in my_list if "imp" not in x]
  set_list=set(my_list)
  no_replica=list(set_list-cleaned_imp)
  print(no_replica)
  return no_replica

Запуск кода, как описано выше

test=imputed_handler(imp_list)

Получаю следующий результат:

['at', 'acm', 'aw', 'ar', 'daa_imp', 'aem', 'as_imp', 'ab_imp', 'aa_imp', 'aum']

Существуют ли лучшие решения? Спасибо за уделенное время, и дайте мне знать, если что-то непонятно 🙂

2 ответа
2

Во-первых, поскольку вы ищете строки, заканчивающиеся на _imp, x.endswith("_imp") вероятно более надежен, чем "imp" in x.

Во-вторых, поскольку мы хотим только удалить _imp когда это суффикс и поскольку мы знаем его длину, мы можем использовать нарезку строки, чтобы удалить последние 4 символа. Это означает, что мы случайно не поворачиваем x_impabc_imp в xabc вместо x_impabc или что-то. Предполагая, что это не то, что мы хотим делать. Может быть, а может и нет, может, мы никогда не получим такой ввод, так что это не имеет значения, я понятия не имею.

Я также думаю, что было бы неплохо передать суффикс в качестве параметра. Может быть, когда-нибудь это изменится. Возможно, другая часть программы аналогичным образом использует другой суффикс. Не помешает подготовиться.

Но в целом ваш подход хорош. Вы найдете _imp одни, вы выясняете, что они заменяют, вы удаляете те, которые были заменены.

Самое интересное, что вы можете выполнить первые два шага одновременно за один проход по списку ввода. Например, вы можете сделать это:

def imputed_handler(my_list, suffix="_imp"):
    my_set = set(my_list)
    cleaned_imp = set(item[:-len(suffix)] for item in my_set if item.endswith(suffix))

    return list(my_set - cleaned_imp)

Хотя в этот момент цикл может быть более понятным:

def imputed_handler(my_list, suffix="_imp"):
    my_set = set(my_list)

    for item in my_list:
        if item.endswith(suffix):
            my_set.discard(item[:-len(suffix)])

    return list(my_set)

  • 5

    Начиная с Python 3.9 вы также можете использовать item.removesuffix(suffix).

    — Келли Банди

  • 1

    @KellyBundy Отлично. Я не нашел этого, так как по какой-то причине я все еще использую 3.8.5, но это полезно знать.

    — Сара Дж.

PEP 8

В Руководство по стилю кода Python перечисляет несколько соглашений, которым должны следовать программы Python. Вещи как:

  • пробелы вокруг бинарных операторов (imp = [...] и set_list = set(my_list)).
  • пробелы после запятой (например, ""), imp))

Побочные эффекты

Вы возвращаете результат из imputed_hander; не должен печатать изнутри.

Типовые подсказки и строки документов

Что def imputed_handler(my_list)? Что оно делает? Что my_list список? Что возвращается?

from typing import List

def imputed_handler(my_list: List[str]) -> List[str]: 
    """
    Filter a list of strings, removing `"xyz"` if `"xyz_imp"` is found.
    Return the filtered list.
    """

Теперь у нас есть (плохое) описание и мы можем видеть типы аргументов и возвращаемых значений.

Улучшенный код

Как упоминалось в ответе Сары Дж, .endswith() предпочтительнее простой проверки, содержится ли строка поиска где-нибудь внутри оригинала.

Преобразование списка в набор, а затем обратно в список разрушает исходный порядок списков. Вот возможное улучшение решения Сары:

from typing import List

def imputed_handler(my_list: List[str], suffix: str = "_imp") -> List[str]:
    """
    A good description here.
    """

    unwanted = {item.removesuffix(suffix) for item in my_list if item.endswith(suffix)}
    return [item for item in my_list if item not in unwanted]

А set создается только из префикса элементов, заканчивающихся на _imp. Это должно привести к меньшему объему памяти, что set(my_list) - cleaned_imp. Поскольку это set, то in оператор $ O (1) $, фильтрация списка выполняется быстро.

  • 2

    Примечание, typing.List теперь не рекомендуется вы можете просто использовать list[str] в Python 3.9+ или импортируйте аннотации из PEP 563 — «Импорт из typing не рекомендуется. Из-за PEP 563 и намерение свести к минимуму влияние набора текста на время выполнения, это исключение не будет генерировать DeprecationWarnings. «

    — Пейлонрайз


  • Еще одна возможная настройка — вместо того, чтобы брать «foo_imp» и генерировать «foo» для удаления, это спрашивать для каждого «foo», есть ли «foo_imp» в наборе. my_set = set(my_list); return [s for s in my_list if s + suffix not in my_set]. Нужно быть осторожным, эквивалентен ли он для таких цепочек, как imputed_handler(["aa", "aa_imp", "aa_imp_imp"]) но я думаю, что это … Это также соответствует вашему описанию «удаление "xyz" если "xyz_imp" найдено «, что является наиболее четким определением, которое я могу придумать.

    — Бени Чернявский-Паскин

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *