Sort typeahead suggestions with the exact input at top
Asked Answered
P

2

11

I am using Twitter typeahead.js 0.10.5 as a suggestion engine. It works great, with one exception, I can't sort the list of suggestions the way I want.

As an example:

            var data =[{"id":1,"value":"no need"},
                        {"id":2,"value":"there is no need"},
                        {"id":3,"value":"in the need of"},
                        {"id":4,"value":"all I need"},
                        {"id":5,"value":"need"},
                        {"id":6,"value":"needs"},
                        {"id":7,"value":"all he needs"},
                        {"id":8,"value":"he needs"},
                        {"id":9,"value":"they need"},
                        {"id":10,"value":"you need"}]

            var suggestion = new Bloodhound({
                datumTokenizer: Bloodhound.tokenizers.obj.whitespace('value'),
                queryTokenizer: Bloodhound.tokenizers.whitespace,
                local: data,
                limit: 20
              });

              suggestion.initialize();

              $('.typeahead').typeahead({
                hint: true,
                autoselect: true,
                highlight: true,
                minLength: 1
              },
              {
                name: 'suggestion',
                displayKey: 'value',
                source: suggestion.ttAdapter(),
                templates: {
                empty: [
                  '<div class="empty-message">',
                  'no suggestion in this map',
                  '</div>'
                ].join('\n'),
                suggestion: Handlebars.compile('<p><span class="suggestion-text">{{value}}</span></p>')
              }

When I type "need" I do get the suggestions ordered by position in array, but I would like to have it ordered by input, meaning the order should be "need","needs","all I need"... When typing "he" it should be "he needs", "all he needs", "all I need" etc.

I know Bloodhound has a sorter option, but I don't know how to use it in this particular situation.

Papuan answered 30/12, 2014 at 11:2 Comment(0)
C
13

You want something along these lines. This will move the exact match to the top. You will want to continue modifying the sorter code to handle string case, whitespace, and how you want non perfect but close matches to be handled.This should get you started.

        var suggestion = new Bloodhound({
            datumTokenizer: Bloodhound.tokenizers.obj.whitespace('value'),
            queryTokenizer: Bloodhound.tokenizers.whitespace,
            local: data,
            limit: 20,
            sorter:function(a, b) { 

                     //get input text
                 var InputString=   $(Selector).val();

                     //move exact matches to top
                 if(InputString==a.value){ return -1;}
                 if(InputString==b.value){return 1;}

                      //close match without case matching
                 if(InputString.toLowerCase() ==a.value.toLowerCase()){ return -1;}
                 if(InputString.toLowerCase()==b.value.toLowerCase()){return 1;} 

                 if( (InputString!=a.value) && (InputString!=b.value)){

                      if (a.value < b.value) {
                         return -1;
                      }
                      else if (a.value > b.value) {
                         return 1;
                      }
                      else return 0;
                 }
              },
          });
Codification answered 5/1, 2015 at 9:34 Comment(2)
Can anyone explain the two values being passed to the function (a, b)? I ran an console log and "a" appeared to be the JSON object, but I couldn't figure out what "b" was.Bandit
the only difference between a and b, is that b would be the next object in the array (directly following a)Codification
A
11

To sort all matches by closeness to the input, you can take the Levenshtein distance of a and b. I just implemented this using fast-levenshtein and it works and performs great.

        sorter: function(a, b) {
            var input_string = $(selector).val();
            return levenshtein.get(a.key, input_string) - levenshtein.get(b.key, input_string);
        }
Arsphenamine answered 18/5, 2016 at 17:14 Comment(2)
An elegant solution and magnitudes faster than the accepted answer.Mycostatin
Is sorter an option in typeahead? Where should I add your snippet?Syncopation

© 2022 - 2024 — McMap. All rights reserved.