import re
def fuzzy_filter(patterns, names):
def match(pattern, words_index=0, pattern_index=0, word_index=0):
return pattern_index == len(pattern) or (
words_index < len(words) and word_index < len(words[words_index])
) and (
pattern[pattern_index] == words[words_index][word_index] and (
match(pattern, words_index, pattern_index + 1, word_index + 1) or
match(pattern, words_index + 1, pattern_index + 1)
) or match(pattern, words_index + 1)
)
for name in names:
words = list(map(str.lower, re.split(r'[\W_]+|(?<=\w)(?=[A-Z])', name)))
if all(map(match, patterns.split())):
yield name
file_names = [
'HelloWorld.csv',
'hello_windsor.pdf',
'some_file_i_need.jpg',
'san_francisco.png',
'Another.file.txt',
'A file name.rar'
]
queries = '''\
hw
hwor
winds
sf
file need
sfr
file
file another
fnrar'''.splitlines()
for query in queries:
print(f'{query} -> {", ".join(fuzzy_filter(query, file_names))}')
aW1wb3J0IHJlCgpkZWYgZnV6enlfZmlsdGVyKHBhdHRlcm5zLCBuYW1lcyk6CiAgICBkZWYgbWF0Y2gocGF0dGVybiwgd29yZHNfaW5kZXg9MCwgcGF0dGVybl9pbmRleD0wLCB3b3JkX2luZGV4PTApOgogICAgICAgIHJldHVybiBwYXR0ZXJuX2luZGV4ID09IGxlbihwYXR0ZXJuKSBvciAoCiAgICAgICAgICAgIHdvcmRzX2luZGV4IDwgbGVuKHdvcmRzKSBhbmQgd29yZF9pbmRleCA8IGxlbih3b3Jkc1t3b3Jkc19pbmRleF0pCiAgICAgICAgKSBhbmQgKAogICAgICAgICAgICBwYXR0ZXJuW3BhdHRlcm5faW5kZXhdID09IHdvcmRzW3dvcmRzX2luZGV4XVt3b3JkX2luZGV4XSBhbmQgKAogICAgICAgICAgICAgICAgbWF0Y2gocGF0dGVybiwgd29yZHNfaW5kZXgsIHBhdHRlcm5faW5kZXggKyAxLCB3b3JkX2luZGV4ICsgMSkgb3IKICAgICAgICAgICAgICAgIG1hdGNoKHBhdHRlcm4sIHdvcmRzX2luZGV4ICsgMSwgcGF0dGVybl9pbmRleCArIDEpCiAgICAgICAgICAgICkgb3IgbWF0Y2gocGF0dGVybiwgd29yZHNfaW5kZXggKyAxKQogICAgICAgICkKCiAgICBmb3IgbmFtZSBpbiBuYW1lczoKICAgICAgICB3b3JkcyA9IGxpc3QobWFwKHN0ci5sb3dlciwgcmUuc3BsaXQocidbXFdfXSt8KD88PVx3KSg/PVtBLVpdKScsIG5hbWUpKSkKICAgICAgICBpZiBhbGwobWFwKG1hdGNoLCBwYXR0ZXJucy5zcGxpdCgpKSk6CiAgICAgICAgICAgIHlpZWxkIG5hbWUKCmZpbGVfbmFtZXMgPSBbCiAgICAnSGVsbG9Xb3JsZC5jc3YnLAogICAgJ2hlbGxvX3dpbmRzb3IucGRmJywKICAgICdzb21lX2ZpbGVfaV9uZWVkLmpwZycsCiAgICAnc2FuX2ZyYW5jaXNjby5wbmcnLAogICAgJ0Fub3RoZXIuZmlsZS50eHQnLAogICAgJ0EgZmlsZSBuYW1lLnJhcicKXQoKcXVlcmllcyA9ICcnJ1wKaHcKaHdvcgp3aW5kcwpzZgpmaWxlIG5lZWQKc2ZyCmZpbGUKZmlsZSBhbm90aGVyCmZucmFyJycnLnNwbGl0bGluZXMoKQoKZm9yIHF1ZXJ5IGluIHF1ZXJpZXM6CiAgICBwcmludChmJ3txdWVyeX0gLT4geyIsICIuam9pbihmdXp6eV9maWx0ZXIocXVlcnksIGZpbGVfbmFtZXMpKX0nKQ==