Python - удалить любой элемент из списка строк, который является подстрокой другого элемента

Итак, начиная со списка строк, как показано ниже

string_list = ['rest', 'resting', 'look', 'look', 'it', 'spit']

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

string_list = ['resting', 'look', 'spit']

У меня есть код, который это делает, но он смущающе уродливый и, вероятно, бесполезно сложный. Есть ли простой способ сделать это в Python?

Ответ 1

Первый строительный блок: подстрока.

Вы можете использовать in для проверки:

>>> 'rest' in 'resting'
True
>>> 'sing' in 'resting'
False

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

def substringSieve(string_list):
    out = []
    for s in string_list:
        if not any([s in r for r in string_list if s != r]):
            out.append(s)
    return out

Вы можете ускорить его, сортируя, чтобы уменьшить количество сравнений (в конце концов, более длинная строка никогда не может быть подстрокой строки более короткой/равной длины):

def substringSieve(string_list):
    string_list.sort(key=lambda s: len(s), reverse=True)
    out = []
    for s in string_list:
        if not any([s in o for o in out]):
            out.append(s)
    return out

Ответ 2

Здесь возможно решение:

string_list = ['rest', 'resting', 'look', 'looked', 'it', 'spit']
def string_set(string_list):
    return set(i for i in string_list 
               if not any(i in s for s in string_list if i != s))

print(string_set(string_list))

выдает:

set(['looked', 'resting', 'spit'])

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

Ответ 3

Еще один лайнер:

[string for string in string_list if len(filter(lambda x: string in x,string_list)) == 1]

должен быть достаточно читабельным, а не пифоном.

Ответ 4

Здесь один метод:

def find_unique(original):
    output = []

    for a in original:
        for b in original:
            if a == b:
                continue     # So we don't compare a string against itself
            elif a in b:
                break
        else:
            output.append(a) # Executed only if "break" is never hit

    return output

if __name__ == '__main__':
    original = ['rest', 'resting', 'look', 'looked', 'it', 'split']
    print find_unique(original)

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

Отпечатает ['resting', 'looked', 'split']

Ответ 5

Вот один-лайнер, который делает то, что вы хотите:

filter(lambda x: [x for i in string_list if x in i and x != i] == [], string_list)

Пример:

>>> string_list = ['rest', 'resting', 'look', 'looked', 'it', 'spit']
>>> filter(lambda x: [x for i in string_list if x in i and x != i] == [], string_list)
['resting', 'looked', 'spit']

Ответ 6

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

for str1 in string_list:
    for str2 in string_list:
        if str1 in str2:
            string_list.remove(str1)

Ответ 7

Вот эффективный способ сделать это (относительно вышеупомянутых решений;)), поскольку этот подход значительно сокращает количество сравнений между элементами списка. Если у меня есть огромный список, я бы определенно пошел с этим, и, конечно, вы можете превратить это решение в функцию лямбда, чтобы он выглядел небольшим:

string_list = ['rest', 'resting', 'look', 'looked', 'it', 'spit']
for item in string_list: 
  for item1 in string_list:
    if item in item1 and item!= item1:
      string_list.remove(item)

print string_list

Вывод:

>>>['resting', 'looked', 'spit']

Надеюсь, что это поможет!

Ответ 8

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

string_list = sorted(string_list)
sieved = []
for i in range(len(string_list) - 1):
    if string_list[i] not in string_list[i+1]:
        sieved.append(string_list[i])