dcc.Dropdown does not respect options manipulation for custom search algorithms

Hi all!

I’m not sure whether this should be a bug report, or a feature request, so any advice would be appreciated. I have a situation where I have a list of items for selection by users that is subject to being misspelled or misrepresented in various ways. In testing, we’ve found that rather than using an exact match (or even close match letter-for-letter) that a fuzzy match algorithm works much better. In our case, we would like to implement a Levenshtein algorithm implemented in the fuzzywuzzy python library. Another option would be something like Soundex, which would match things like “Jeff” and “Geoff”.

When browsing the documentation for dcc.Dropdown I determined that I should be able to set options returned using a callback function. All good so far. I implemented my alternative fuzzywuzzy algorithm and provided the top 5 matches to what the user typed in, as the returned, updated options list. Unfortunately, the dropdown list apparently has an additional step outside of my control (unless I’ve missed a parameter that allows for this) that filter out based (apparently) on an “in - list”, and if an option I return does not contain the string (partial or full) that the user enters, then the option is filtered out of the options list. So, “Jeff” will never return “Geoff” as an option.

Here’s a simple example of the problem I’m seeing:

from dash import Dash, dcc, html, dcc, Input, Output, State, MATCH, ALL
from dash.exceptions import PreventUpdate
import pandas as pd

from fuzzywuzzy import process

app = Dash(__name__, suppress_callback_exceptions=True)


options = [
    {"label": 'Felicity Davies', "value": 'Felicity Davies'},
    {"label": 'Emma Cornish', "value": 'Emma Cornish'},
    {"label": 'Faith Davidson', "value": 'Faith Davidson'},
    {"label": 'Emily Coleman', "value": 'Emily Coleman'},
    {"label": 'Ella Clarkson', "value": 'Ella Clarkson'},
    {"label": 'Elizabeth Clark', "value": 'Elizabeth Clark'},
    {"label": 'Dorothy Churchill', "value": 'Dorothy Churchill'},
    {"label": 'Donna Chapman', "value": 'Donna Chapman'},
    {"label": 'Diane Carr', "value": 'Diane Carr'},
    {"label": 'Harry Campbell', "value": 'Harry Campbell'},
    {"label": 'Gordon Cameron', "value": 'Gordon Cameron'},
    {"label": 'Gavin Butler', "value": 'Gavin Butler'},
    {"label": 'Frank Burgess', "value": 'Frank Burgess'},
    {"label": 'Evan Buckland', "value": 'Evan Buckland'},
    {"label": 'Eric Brown', "value": 'Eric Brown'},
    {"label": 'Edward Bower', "value": 'Edward Bower'},
]


options_df = pd.DataFrame(options)

app.layout = html.Div([
    html.Button("Add Filter", id="add-filter", n_clicks=0),
    html.Div(id='dropdown-container', children=[]),
    html.Div(id='dropdown-container-output')
])

app = Dash(__name__)
app.layout = html.Div([
        html.Div('List of All Options:', style={'color': 'blue', 'font-weight': 'bold'}),
        html.Div(id='optionslist', style={'whiteSpace': 'pre-line'}, children=", ".join(options_df['value'])),
        html.Br(),
        html.Br(),
        html.Div('Options that should be available', style={'color': 'blue', 'font-weight': 'bold'}),
        html.Div(id='optionstext', style={'whiteSpace': 'pre-line'}, children=", ".join(options_df['value'])),
        html.Hr(),
        html.Div([
            "Select all Products/Technologies Involved in the Discussion:",
            dcc.Dropdown(id="my-multi-dynamic-dropdown", multi=True),
        ]),
])


@app.callback(
    Output("my-multi-dynamic-dropdown", "options"),
    Output('optionstext', 'children'),
    Input("my-multi-dynamic-dropdown", "search_value"),
    State("my-multi-dynamic-dropdown", "value")
)
def update_multi_options(search_value, value):
    if not search_value:
        raise PreventUpdate
    # Make sure that the set values are in the option list, else they will disappear
    # from the shown select list, but still part of the `value`.
    # print(f'''options labels: {options_df['label']}''')
    fuzzy_out = process.extract(search_value, choices=options_df['label'], limit=5)
    fuzz_list = []
    for fuzz_ret in fuzzy_out:
        fuzz_list.append(fuzz_ret[0])

    print(f'''Fuzz List:{fuzz_list}''')

    for o in options:
        print(f'''Options labels:{o["label"]}''')
        if o["label"] in fuzz_list:
            print(f'YES! {o["label"]} is in the fuzz_list!')

    items = [o for o in options if o['label'] in fuzz_list or o["value"] in (value or [])]
    print(f'Items selected: {items}')
    outlist = ", ".join(fuzz_list)

    return items, outlist


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

And here is what the app looks like when initially running:
dcc-dropdown-ex1

As you enter names, you will see the “Options that should be available” change as you type. You’ll also see what the dcc.Dropdown provides as options. To see the problem, try typing “Ivan”. You’ll note that in the “Options that should be available” “Evan Buckland” is the second option (which is what I want)…but it does not show up in the options list for selection!

I don’t know whether this was intentional, and I’m asking for feature to be added, or whether it was simply an oversight. I’m concerned this additional filtering is going on at the React level, and may be more difficult to resolve. But the documentation implies that a set of options can be manipulated and returned within the callback for dcc.Dropdown, and this is misleading at best.

If I’ve missed something and I’m doing this incorrectly, please let me know! Otherwise, I’m happy to log a bug and would appreciate suggestions on whether to classify as a Bug or a Feature request. Thanks!

1 Like

I think you should return value for my-multi-dynamic-dropdown in your callback also. Something as below:

@app.callback(
    Output("my-multi-dynamic-dropdown", "options"),
    Output("my-multi-dynamic-dropdown", "value"),
    Output('optionstext', 'children'),
    Input("my-multi-dynamic-dropdown", "search_value"),
    State("my-multi-dynamic-dropdown", "value")
)
def update_multi_options(search_value, value):
    if not search_value:
        raise PreventUpdate
    # Make sure that the set values are in the option list, else they will disappear
    # from the shown select list, but still part of the `value`.
    # print(f'''options labels: {options_df['label']}''')
    fuzzy_out = process.extract(search_value, choices=options_df['label'], limit=5)
    fuzz_list = []
    for fuzz_ret in fuzzy_out:
        fuzz_list.append(fuzz_ret[0])

    print(f'''Fuzz List:{fuzz_list}''')

    for o in options:
        print(f'''Options labels:{o["label"]}''')
        if o["label"] in fuzz_list:
            print(f'YES! {o["label"]} is in the fuzz_list!')

    items = [{'label':x, 'value':x} for x in fuzz_list]
    print(f'Items selected: {items}')
    outlist = ", ".join(fuzz_list)

    return items, items, outlist

if __name__ == "__main__":
    app.run_server(debug=False)

Because you are setting your dropdown as multi=True so when you type something all of 5 options that showed in optionstext will be showed all. But I see a problem that it can not type long word like Ivan when using this code so I think maybe you could concern about adding dcc.Input and use value of it to return dropdown options. Something as below:

from dash import Dash, dcc, html, dcc, Input, Output, State, MATCH, ALL
from dash.exceptions import PreventUpdate
import pandas as pd

from fuzzywuzzy import process

app = Dash(__name__, suppress_callback_exceptions=True)


options = [
    {"label": 'Felicity Davies', "value": 'Felicity Davies'},
    {"label": 'Emma Cornish', "value": 'Emma Cornish'},
    {"label": 'Faith Davidson', "value": 'Faith Davidson'},
    {"label": 'Emily Coleman', "value": 'Emily Coleman'},
    {"label": 'Ella Clarkson', "value": 'Ella Clarkson'},
    {"label": 'Elizabeth Clark', "value": 'Elizabeth Clark'},
    {"label": 'Dorothy Churchill', "value": 'Dorothy Churchill'},
    {"label": 'Donna Chapman', "value": 'Donna Chapman'},
    {"label": 'Diane Carr', "value": 'Diane Carr'},
    {"label": 'Harry Campbell', "value": 'Harry Campbell'},
    {"label": 'Gordon Cameron', "value": 'Gordon Cameron'},
    {"label": 'Gavin Butler', "value": 'Gavin Butler'},
    {"label": 'Frank Burgess', "value": 'Frank Burgess'},
    {"label": 'Evan Buckland', "value": 'Evan Buckland'},
    {"label": 'Eric Brown', "value": 'Eric Brown'},
    {"label": 'Edward Bower', "value": 'Edward Bower'},
]


options_df = pd.DataFrame(options)

app.layout = html.Div([
    html.Button("Add Filter", id="add-filter", n_clicks=0),
    html.Div(id='dropdown-container', children=[]),
    html.Div(id='dropdown-container-output')
])

app = Dash(__name__)
app.layout = html.Div([
        html.Div('List of All Options:', style={'color': 'blue', 'font-weight': 'bold'}),
        html.Div(id='optionslist', style={'whiteSpace': 'pre-line'}, children=", ".join(options_df['value'])),
        html.Br(),
        html.Br(),
        html.Div('Options that should be available', style={'color': 'blue', 'font-weight': 'bold'}),
        html.Div(id='optionstext', style={'whiteSpace': 'pre-line'}, children=", ".join(options_df['value'])),
        html.Hr(),
        html.Div([dcc.Input(id='inputt')]),
        html.Div([
            "Select all Products/Technologies Involved in the Discussion:",
            dcc.Dropdown(id="my-multi-dynamic-dropdown", multi=True),
        ]),
])


@app.callback(
    Output("my-multi-dynamic-dropdown", "options"),
    Output('optionstext', 'children'),
    Input("inputt", "value"),
    State("my-multi-dynamic-dropdown", "value")
)
def update_multi_options(search_value, value):
    if not search_value:
        raise PreventUpdate
    # Make sure that the set values are in the option list, else they will disappear
    # from the shown select list, but still part of the `value`.
    # print(f'''options labels: {options_df['label']}''')
    fuzzy_out = process.extract(search_value, choices=options_df['label'], limit=5)
    fuzz_list = []
    for fuzz_ret in fuzzy_out:
        fuzz_list.append(fuzz_ret[0])

    print(f'''Fuzz List:{fuzz_list}''')

    for o in options:
        print(f'''Options labels:{o["label"]}''')
        if o["label"] in fuzz_list:
            print(f'YES! {o["label"]} is in the fuzz_list!')

    items = [{'label':x, 'value':x} for x in fuzz_list]
    print(f'{items}')
    outlist = ", ".join(fuzz_list)

    return items, outlist

if __name__ == "__main__":
    app.run_server(debug=False)

Thank-you @hoatran , for your thoughtful reply, and while I agree with your comment regarding “values”, the suggestion of having a user type in an input box, then click in the dropdown to see new suggestions is a dreadful User Experience and doesn’t match my own experience in using this type of interface. I see no reason for the interface to add an additional filter when the code allows me to explicitly return a list of new options. That is my description of the “Bug” or “Feature” I wish to have fixed or developed. Your suggestion does side step the problem, but does not provide a User Interface that works as Users would expect. Do you know whether the additional “filtering of options” is occurring within the React level? If so, I understand that the requested change may be difficult or not possible.

1 Like

Yep, it’s just my opinion. Can I have question that does it need to input long word like Ivan or just one letter like v or I? Below code worked with one letter but after input letter, it disappeared so I could not type the long word. I’m just a learner of Dash so I have many things to know.

from dash import Dash, dcc, html, dcc, Input, Output, State, MATCH, ALL
from dash.exceptions import PreventUpdate
import pandas as pd

from fuzzywuzzy import process

app = Dash(__name__, suppress_callback_exceptions=True)


options = [
    {"label": 'Felicity Davies', "value": 'Felicity Davies'},
    {"label": 'Emma Cornish', "value": 'Emma Cornish'},
    {"label": 'Faith Davidson', "value": 'Faith Davidson'},
    {"label": 'Emily Coleman', "value": 'Emily Coleman'},
    {"label": 'Ella Clarkson', "value": 'Ella Clarkson'},
    {"label": 'Elizabeth Clark', "value": 'Elizabeth Clark'},
    {"label": 'Dorothy Churchill', "value": 'Dorothy Churchill'},
    {"label": 'Donna Chapman', "value": 'Donna Chapman'},
    {"label": 'Diane Carr', "value": 'Diane Carr'},
    {"label": 'Harry Campbell', "value": 'Harry Campbell'},
    {"label": 'Gordon Cameron', "value": 'Gordon Cameron'},
    {"label": 'Gavin Butler', "value": 'Gavin Butler'},
    {"label": 'Frank Burgess', "value": 'Frank Burgess'},
    {"label": 'Evan Buckland', "value": 'Evan Buckland'},
    {"label": 'Eric Brown', "value": 'Eric Brown'},
    {"label": 'Edward Bower', "value": 'Edward Bower'},
]


options_df = pd.DataFrame(options)

app.layout = html.Div([
    html.Button("Add Filter", id="add-filter", n_clicks=0),
    html.Div(id='dropdown-container', children=[]),
    html.Div(id='dropdown-container-output')
])

app = Dash(__name__)
app.layout = html.Div([
        html.Div('List of All Options:', style={'color': 'blue', 'font-weight': 'bold'}),
        html.Div(id='optionslist', style={'whiteSpace': 'pre-line'}, children=", ".join(options_df['value'])),
        html.Br(),
        html.Br(),
        html.Div('Options that should be available', style={'color': 'blue', 'font-weight': 'bold'}),
        html.Div(id='optionstext', style={'whiteSpace': 'pre-line'}, children=", ".join(options_df['value'])),
        html.Hr(),
        html.Div([
            "Select all Products/Technologies Involved in the Discussion:",
            dcc.Dropdown(id="my-multi-dynamic-dropdown", multi=True),
        ]),
])


@app.callback(
    Output("my-multi-dynamic-dropdown", "options"),
    Output("my-multi-dynamic-dropdown", "value"),
    Output('optionstext', 'children'),
    Input("my-multi-dynamic-dropdown", "search_value"),
    State("my-multi-dynamic-dropdown", "value")
)

def update_multi_options(search_value,value):
    if not search_value:
        raise PreventUpdate       
    else:
        fuzzy_out = process.extract(search_value, choices=options_df['label'], limit=5)
        fuzz_list = []
        for fuzz_ret in fuzzy_out:
            fuzz_list.append(fuzz_ret[0])
        
        items = [{'label':x, 'value':x} for x in fuzz_list]
        outlist = ", ".join(fuzz_list)
        return items, [], outlist

if __name__ == "__main__":
    app.run_server(debug=False)

@hoatran , No problem with your reply - perfectly valid approach from a code viewpoint that side-steps the issue that I pointed out…I just don’t like the User Experience it creates. In fact, you lose all search results because the dropdown doesn’t appear if you’re typing in a simple text box. My code did display them…but again, that’s not what users would expect. They would expect their selections available to be filtered by some search criteria as they type.

I do appreciate your effort and as you can tell - no one else has even tried to address this, so I will likely need to open a bug report if I wish this to be fixed…or if I get really inspired, I may develop a Dash component that works that way I need it to, and then push it up to the Community as a Community component…but honestly, I’m a little low on time to take that on at this time.

With regard to your issue of typing in more than one letter…I’m a bit confused. I don’t see that issue when I run the original code I posted, nor when I run the code that you posted back. I can type as much as I wish in either the text box you provided, or the dropdown widget in my own code. In fact, that’s how I was testing my search results (obviously, the available selections from Dash’s dropdown were hiding results like “Emma Cornish” when I typed “Imma”, so I put in the text box under the “Options that should be available” label, so that I could make sure my own search provided was working correctly and returning a set of options that I expected. It was also how I know there must be some other filtering that is going on at the component level (likely in the underlying React implementation of the component). My complaint is that the description of how this is supposed to work allows me be provide my own list of options - and the options I return are being manipulated after I return them to the component after the callback code has finished. This prevents me from controlling it from python and Dash. That, to me, seems to be a bug, but the developers may feel that it’s a new feature request. That’s what I was trying to determine - what’s the best way to ask to have this solved? (Of course, it’s certainly possible that if no one else wants this to be solved…it may not get solved…and I’d be right back to the point of having to develop it myself, if I really want it. At least there’s always that option :wink:

Again, thanks for taking a look at this! Good luck with your Dash efforts! It’s a great environment and tool!

1 Like

hi @geneblandjr
Interesting question. I don’t recall anyone having tried that before. There might be a problem with two commands happening at the same time.

  1. the Dropdown natively looks for the current options list as the user types text into the dropdown.
  2. But at the same time we’re telling the app to upload a new options list.

I’m not sure if there is a way around that… There might be a way by using the search key within the options property. For example, you wrote:

I took your code and added the search key to the third option from last in the options list.

from dash import Dash, dcc, html, dcc, Input, Output, State, MATCH, ALL
from dash.exceptions import PreventUpdate
import pandas as pd

from fuzzywuzzy import process

app = Dash(__name__, suppress_callback_exceptions=True)


options = [
    {"label": 'Felicity Davies', "value": 'Felicity Davies'},
    {"label": 'Emma Cornish', "value": 'Emma Cornish'},
    {"label": 'Faith Davidson', "value": 'Faith Davidson'},
    {"label": 'Emily Coleman', "value": 'Emily Coleman'},
    {"label": 'Ella Clarkson', "value": 'Ella Clarkson'},
    {"label": 'Elizabeth Clark', "value": 'Elizabeth Clark'},
    {"label": 'Dorothy Churchill', "value": 'Dorothy Churchill'},
    {"label": 'Donna Chapman', "value": 'Donna Chapman'},
    {"label": 'Diane Carr', "value": 'Diane Carr'},
    {"label": 'Harry Campbell', "value": 'Harry Campbell'},
    {"label": 'Gordon Cameron', "value": 'Gordon Cameron'},
    {"label": 'Gavin Butler', "value": 'Gavin Butler'},
    {"label": 'Frank Burgess', "value": 'Frank Burgess'},
    {"label": 'Evan Buckland', "value": 'Evan Buckland', "search": "Ivan"},
    {"label": 'Eric Brown', "value": 'Eric Brown'},
    {"label": 'Edward Bower', "value": 'Edward Bower'},
]


options_df = pd.DataFrame(options)

app.layout = html.Div([
        html.Div('List of All Options:', style={'color': 'blue', 'font-weight': 'bold'}),
        html.Div(id='optionslist', style={'whiteSpace': 'pre-line'}, children=", ".join(options_df['value'])),
        html.Br(),
        html.Br(),
        html.Div('Options that should be available', style={'color': 'blue', 'font-weight': 'bold'}),
        html.Div(id='optionstext', style={'whiteSpace': 'pre-line'}, children=", ".join(options_df['value'])),
        html.Hr(),
        html.Div([
            "Select all Products/Technologies Involved in the Discussion:",
            dcc.Dropdown(id="my-multi-dynamic-dropdown", multi=True),
        ]),
])


@app.callback(
    Output("my-multi-dynamic-dropdown", "options"),
    Output('optionstext', 'children'),
    Input("my-multi-dynamic-dropdown", "search_value"),
    State("my-multi-dynamic-dropdown", "value")
)
def update_multi_options(search_value, value):
    if not search_value:
        raise PreventUpdate
    # Make sure that the set values are in the option list, else they will disappear
    # from the shown select list, but still part of the `value`.
    # print(f'''options labels: {options_df['label']}''')
    fuzzy_out = process.extract(search_value, choices=options_df['label'], limit=5)
    fuzz_list = []
    for fuzz_ret in fuzzy_out:
        fuzz_list.append(fuzz_ret[0])

    print(f'''Fuzz List:{fuzz_list}''')

    for o in options:
        print(f'''Options labels:{o["label"]}''')
        if o["label"] in fuzz_list:
            print(f'YES! {o["label"]} is in the fuzz_list!')

    items = [o for o in options if o['label'] in fuzz_list or o["value"] in (value or [])]
    print(f'Items selected: {items}')
    outlist = ", ".join(fuzz_list)
    print(outlist)

    return items, outlist


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

If you run the app, and type Ivan in the dropdown, Evan Buckland will be the only option that displays.

The search key is limited to a string though (read more in dropdown properties), so I’m not sure you can use it to get the complete result you’re looking for.

1 Like

@adamschroeder Hi! Thanks for taking a look at this. Yea, that is an interesting thought…I suppose I could peek at the results from my fuzzy search and modify (by inplace editing) the options that my fuzzy search returns…but seems really cumbersome, and potentially inefficient.

Your reply did have me go out and look at all the options, and I thought I had found the answer…I saw that you can turn off the “search” functionality by setting “searchable=False” on the dcc.Dropdown. That really screwed things up and presents a situation where absolutely no options are selectable…ever. That was unexpected and might be something else that needs to be fixed, though admittedly, it could simply be that I’ve created a situation with mismatched options.

@adamschroeder I wanted to let you know that your suggestion led me to what I think is a potential workaround for this issue. Using your approach of adding the entered field to the “search” parameter, I extrapolated and created what I need. I’m concerned it may not be performant with a large set of options…but that remains to be seen. Others may be able to optimize my approach to make it performant.

At the same time, I’d love to see this capability natively built in, but if the demand simply is not that high for this use case, I totally understand. At least I have a viable demo, which was my goal in the first place. Code follows:

from dash import Dash, dcc, html, dcc, Input, Output, State, MATCH, ALL
from dash.exceptions import PreventUpdate
import pandas as pd

from fuzzywuzzy import process

app = Dash(__name__, suppress_callback_exceptions=True)

options = [
    {"label": 'Felicity Davies', "value": 'Felicity Davies', "search": ""},
    {"label": 'Emma Cornish', "value": 'Emma Cornish', "search": ""},
    {"label": 'Faith Davidson', "value": 'Faith Davidson', "search": ""},
    {"label": 'Emily Coleman', "value": 'Emily Coleman', "search": ""},
    {"label": 'Ella Clarkson', "value": 'Ella Clarkson', "search": ""},
    {"label": 'Elizabeth Clark', "value": 'Elizabeth Clark', "search": ""},
    {"label": 'Dorothy Churchill', "value": 'Dorothy Churchill', "search": ""},
    {"label": 'Donna Chapman', "value": 'Donna Chapman', "search": ""},
    {"label": 'Diane Carr', "value": 'Diane Carr', "search": ""},
    {"label": 'Harry Campbell', "value": 'Harry Campbell', "search": ""},
    {"label": 'Gordon Cameron', "value": 'Gordon Cameron', "search": ""},
    {"label": 'Gavin Butler', "value": 'Gavin Butler', "search": ""},
    {"label": 'Frank Burgess', "value": 'Frank Burgess', "search": ""},
    {"label": 'Evan Buckland', "value": 'Evan Buckland', "search": ""},
    {"label": 'Eric Brown', "value": 'Eric Brown', "search": ""},
    {"label": 'Edward Bower', "value": 'Edward Bower', "search": ""},
]


options_df = pd.DataFrame(options)

app.layout = html.Div([
        html.Div('List of All Options:', style={'color': 'blue', 'font-weight': 'bold'}),
        html.Div(id='optionslist', style={'whiteSpace': 'pre-line'}, children=", ".join(options_df['value'])),
        html.Br(),
        html.Br(),
        html.Div('Options that should be available', style={'color': 'blue', 'font-weight': 'bold'}),
        html.Div(id='optionstext', style={'whiteSpace': 'pre-line'}, children=", ".join(options_df['value'])),
        html.Hr(),
        html.Div([
            "Select all Products/Technologies Involved in the Discussion:",
            dcc.Dropdown(id="my-multi-dynamic-dropdown", multi=True),
        ]),
])


@app.callback(
    Output("my-multi-dynamic-dropdown", "options"),
    Output('optionstext', 'children'),
    Input("my-multi-dynamic-dropdown", "search_value"),
    State("my-multi-dynamic-dropdown", "value")
)
def update_multi_options(search_value, value):
    if not search_value:
        raise PreventUpdate
    # Make sure that the set values are in the option list, else they will disappear
    # from the shown select list, but still part of the `value`.
    # print(f'''options labels: {options_df['label']}''')
    fuzzy_out = process.extract(search_value, choices=options_df['label'], limit=5)
    fuzz_list = []
    fuzz_order = []
    for fuzz_ret in fuzzy_out:
        fuzz_list.append(fuzz_ret[0])
        fuzz_order.append(fuzz_ret[1])

    ###### Start of work-around.
    #  For the set of options, we need the "search" option that is standard to the dropdown object,
    # and because I need to sort the results along the resulting best to worst options returned from
    # the fuzzywuzzy process, I also add an 'order' item to each so the resulting list can be ordered
    # in the best-to-worst match.  Note that the 'order' items will need to be stripped after sorting, but
    # before we return the options list from the callback!

    # Here I'm building the temporary lookups for matches of the options, and adding the current value of
    # what the user typed in, as the search_value for the 'search'option.  This ensures that the option will be
    # retained in the resulting options list.  We also temporarily store the value of the strength of the
    # match in the 'order' parameter.
    for o in options:
        o['order'] = 0
        for opt, ord in zip(fuzz_list, fuzz_order):
            if o['label'] == opt:
                o['search'] = search_value
                o['order'] = ord

    # Here is we collect the list of items we will return from the temporary 'o' list of options.  Note that we
    # include the 'order' in this list of items.
    items = [o for o in options if o['label'] in fuzz_list or o["value"] in (value or [])]

    # Here we sort the result items in reversed order of the 'order' member because the order is (more or less)
    # the strength of the match where higher is better.  We wish to present the highest to lowest.
    items = sorted(items, reverse=True, key=lambda x: x['order'])

    # The options list that we are returning must match the prototype of the options list for the Dash Dropdown, which
    # does not include 'order', so here we strip it out.
    for item in items:
        del item['order']

    outlist = ", ".join(fuzz_list)

    return items, outlist


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

Thanks so much to @adamschroeder for making the suggestion for the search string. It was the enabler for expanding this approach!

1 Like

A gemstone of a solution :tada: Thanks for sharing your code @geneblandjr

I’m glad you got it to work.