git.lucas.co / hou-control
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]))