Python3: сопоставление элементов между двумя списками на основе подстрок - PullRequest
1 голос
/ 14 марта 2019

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

    list1 = ['/../../abc_file1.txt',
             '/../../abc_extrafile1.txt',
             '/../../abc_file2.txt',
             '/../../abc_file3.txt',
             '/../../abc_extrafile3.txt']

А затем у меня есть другой список

    ['file1', 'extrafile1', 'file2', 'file3', 'extrafile3']

Итак, я хотел бы получить соответствие, которое генерирует словарь, например:

    {'file1': '/../../abc_file1.txt',
     'extrafile1': '/../../abc_extrafile1.txt',
     'file2': '/../../abc_file2.txt',
     'file3': '/../../abc_file3.txt',
     'extrafile3': '/../../abc_extrafile3.txt'}

Так что есть некоторые совпадения между именами файлов, и я должен быть осторожен с этим.

Есть несколько способов сделать что-то подобное, но я не был уверен, какой из них наиболее эффективен для сопоставления списков порядка 1000 или 10000 записей.Кажется, что это может быть сделано с помощью словарного понимания или лямбды, но кажется немного сложным.Я мог бы написать простой цикл, но это не кажется особенно эффективным.

Любые предложения о том, как справиться с подобной проблемой соответствия.

Ответы [ 2 ]

1 голос
/ 14 марта 2019

Вы можете запустить dict comprehension, как вы предложили, и проверить split первого элемента списка (чтобы учесть перекрытия) и удалить расширения:

list1 = ['/../../abc_file1.txt',
             '/../../abc_extrafile1.txt',
             '/../../abc_file2.txt',
             '/../../abc_file3.txt',
             '/../../abc_extrafile3.txt']

list2 = ['file1', 'extrafile1', 'file2', 'file3', 'extrafile3']

my_dict = {k: v for v in list1 for k in list2 if k == v.split('_')[1][:-4]}

вывод :

{'file1': '/../../abc_file1.txt', 'extrafile1': '/../../abc_extrafile1.txt', 'file2': '/../../abc_file2.txt', 'file3': '/../../abc_file3.txt', 'extrafile3': '/../../abc_extrafile3.txt'}
0 голосов
/ 19 марта 2019

Понимания - это просто более простой способ написания циклов построения коллекций.Легче на глаз, не обязательно эффективно.

В ответе @ matt-b dict comprehension скрывает двойной цикл for, что делает понимание довольно медленным с большими списками (сложность n-квадрат).

Ваша конкретная проблема может быть решена с помощью простого цикла, сохраняя сложность линейной.

С этим входом:

size = 1000
list1 = [ '/../../abc_file' + str(i) + '.txt' for i in range(size) ]
list2 = [ 'file' + str(i) for i in range(size) ]

dict comprehension занимает около 500 мс на моей машине:

my_dict = {k: v for v in list1 for k in list2 if k == v.split('_')[1][:-4]}

# 1 loop, best of 3: 516 ms per loop

Следующая версия быстрее, около 1 мс:

res = { k: None for k in list2 }
for v in list1:
    name = v.split('_')[-1][:-4]
    if name in res:
        res[name] = v

# 100 loops, best of 3: 1.15 ms per loop

С этой структурой легко поддерживать несколько совпадений при необходимости:

res = { k: [] for k in list2 }
for v in list1:
    name = v.split('_')[-1][:-4]
    if name in res:
        res[name].append(v)

# 100 loops, best of 3: 1.54 ms per loop

Вы также можете сохранить первое совпадение, сверяя текущие значения res[name] с None.

...