Fuzzy match in dropdown values/prevent automatic options filtering

In my application I’m using a searchable dropdown dynamically populated by values obtained through an API. The idea is that the user types an address and gets a suggestion.

This works pretty well BUT it has a big issue. In fact, the dropdown element performs an additional filtering on the options provided via the callback to detect if there’s a match or not, which may not be optimal in case the input is just slightly different.

For example consider this

Screenshot 2024-06-20 at 12.55.54

If I change the “ü” with a “u” then the dropdown finds no match

Screenshot 2024-06-20 at 12.56.01

which is annoying because…spell mistakes happen! :slight_smile:

Note that the underlying API is doing everything properly, as the options are exactly the same in the first and second examples, and in fact they show up in the debug window. It just decides not to render them because of this additional filtering.

I tried with the datalist component linked to an Input component but the problem was the same because some filtering is performed anyway.

I’m trying to think what would be the best solution. Of course I could separate the input and dropdown features in two different components, and this will work just fine, but I really want to keep the application compact and have a single element for both features.

Do you know if there’s any solution to this?

here is a simple demo with thefuzz and feffery-antd-components:

PS: use pip install feffery-antd-components --pre -U to install the latest preview version of fac

demo

import dash
from dash import html
from thefuzz import fuzz
from thefuzz import process
import feffery_antd_components as fac
from dash.dependencies import Input, Output

app = dash.Dash(__name__)

app.layout = html.Div(
    [
        fac.AntdSelect(
            id="fuzz-search-select",
            placeholder="type your keyword here",
            options=[],
            locale="en-us",
            autoSpin=True,
            debounceWait=200,
            optionFilterMode="remote-match",
            style={"width": 250},
        )
    ],
    style={"padding": 50},
)


@app.callback(
    Output("fuzz-search-select", "options"),
    Input("fuzz-search-select", "debounceSearchValue"),
)
def remote_fuzz_search(debounceSearchValue):
    # full demo options list
    options = [
        "Python",
        "Java",
        "C++",
        "C#",
        "PHP",
        "JavaScript",
        "Ruby",
        "Go",
        "Rust",
        "Swift",
        "Kotlin",
        "Dart",
        "Scala",
    ]

    if debounceSearchValue:
        # Try to return all options with fuzzy matching scores greater than 0
        return [
            item[0]
            for item in process.extract(
                debounceSearchValue.lower(),
                [s.lower() for s in options],
                limit=2,
                scorer=fuzz.token_sort_ratio,
            )
            if item[1] > 0
        ]

    return options


if __name__ == "__main__":
    app.run(debug=True)

related document:

https://fac-next.feffery.tech/AntdSelect

1 Like

This is an interestin approach, but I’m not sure I want to install a new library just for that :slight_smile:

Anyway I think your approach goes even further. I don’t need to implement the fuzzy match logic, because this is already done by the API. For example if I submit Pthon to the API, I’ll still get Python as response, so the option is inside the Dropdown options. The problem is that then the Dropdown component automatically filters those options as result of the searchable behaviour.
I’d only need the dropdown to behave as a Select + Input component together

The key is that I can fully take over the option search function in AntdSelect, which is difficult to achieve based on other libraries. You can also learn more through my official documentation website (temporarily need to work with the browser translation plug-in), which is a mature Dash component library maintained by me, with super functions, and is widely used in China.