git clone https://git.lucas.co/hou-control.git
python3.11libs/fuzzyfinder/main.py (2.1K)
1 # -*- coding: utf-8 -*-
2 import re
3 from . import export
4
5
6 @export
7 def fuzzyfinder(input, collection, accessor=lambda x: x, sort_results=True, ignore_case=True):
8 """
9 Args:
10 input (str): A partial string which is typically entered by a user.
11 collection (iterable): A collection of strings which will be filtered
12 based on the `input`.
13 accessor (function): If the `collection` is not an iterable of strings,
14 then use the accessor to fetch the string that
15 will be used for fuzzy matching.
16 sort_results (bool): The suggestions are sorted by considering the
17 smallest contiguous match, followed by where the
18 match is found in the full string. If two suggestions
19 have the same rank, they are then sorted
20 alpha-numerically. This parameter controls the
21 *last tie-breaker-alpha-numeric sorting*. The sorting
22 based on match length and position will be intact.
23 ignore_case (bool): If this parameter is set to False, the filtering
24 is case-sensitive.
25
26 Returns:
27 suggestions (generator): A generator object that produces a list of
28 suggestions narrowed down from `collection` using the `input`.
29 """
30 suggestions = []
31 input = str(input) if not isinstance(input, str) else input
32 pat = ".*?".join(map(re.escape, input))
33 pat = "(?=({0}))".format(pat) # lookahead regex to manage overlapping matches
34 regex = re.compile(pat, re.IGNORECASE if ignore_case else 0)
35 for item in collection:
36 r = list(regex.finditer(accessor(item)))
37 if r:
38 best = min(r, key=lambda x: len(x.group(1))) # find shortest match
39 suggestions.append((len(best.group(1)), best.start(), accessor(item), item))
40
41 if sort_results:
42 return (z[-1] for z in sorted(suggestions))
43 else:
44 return (z[-1] for z in sorted(suggestions, key=lambda x: x[:2]))