8

I'd like to sort my jQuery Autocomplete UI results based on where in the string the match occurs. The results where the match is the first letter should be prioritized above results in which the match is in the middle of the string.

A search for "M" should return:

Matt, Michael, Sam, Tim, Adam, Benjamin

Instead, since it's just returning the items in alphabetical order right now, I'm getting this:

Adam, Benjamin, Matt, Michael, Sam, Tim

Unfortunately, it looks like Autocomplete UI doesn't have any options for doing something like this, instead it just presents the results in the order it received them. Having MySql do the sorting isn't an option since all the possible matches are preloaded so that I'm not making calls to the database with every keystroke. Has anybody done anything like this?

Andrew Whitaker
  • 124,656
  • 32
  • 289
  • 307
blim8183
  • 768
  • 1
  • 8
  • 23

3 Answers3

18

You can provide any local filtering logic you'd like by providing the source option a function instead of a simple array. Here's a quick example that will do the basics of what you want:

var source = ['Adam', 'Benjamin', 'Matt', 'Michael', 'Sam', 'Tim'];
$("input").autocomplete({
    source: function (request, response) {
        var term = $.ui.autocomplete.escapeRegex(request.term)
            , startsWithMatcher = new RegExp("^" + term, "i")
            , startsWith = $.grep(source, function(value) {
                return startsWithMatcher.test(value.label || value.value || value);
            })
            , containsMatcher = new RegExp(term, "i")
            , contains = $.grep(source, function (value) {
                return $.inArray(value, startsWith) < 0 &&
                    containsMatcher.test(value.label || value.value || value);
            });

        response(startsWith.concat(contains));
    }
});

Example: http://jsfiddle.net/zkVrs/

Basically, the logic is to build up an array of matches that start with the term, and then concatenate that with matches that contain the term but don't start with it.

Performance could be a problem here, especially with the $.inArray call. Might be a better way to accomplish that portion, but hopefully that will give you a good starting point.

Andrew Whitaker
  • 124,656
  • 32
  • 289
  • 307
  • 1
    Ah, that makes sense. Here's my variation on your idea: source: function(request, response){ var results = jQuery.ui.autocomplete.filter(data, request.term); results = results.sort(function(a, b){ return a.title.toLowerCase().indexOf(request.term.toLowerCase()) - b.title.toLowerCase().indexOf(request.term.toLowerCase()); }); response(results.slice(0,10)); } – blim8183 Nov 28 '11 at 22:12
  • @blim8183: Yep, that would definitely work too and might be faster – Andrew Whitaker Nov 28 '11 at 22:15
  • Oi, is there a way to format code in comments? That's almost unreadable, glad you could decipher it. Thanks for your help! – blim8183 Nov 28 '11 at 22:17
  • 1
    @blim8183: Sure thing! Just use the backtick (`) – Andrew Whitaker Nov 28 '11 at 22:17
  • But this doesn't seem to work for multiple values, jQuery autocomplete.?? – Javier Brooklyn Feb 08 '13 at 19:51
  • @JavierBrooklyn I'm not sure, I didn't write it with that in mind. It might be worth opening another question if you're having trouble. – Andrew Whitaker Feb 08 '13 at 22:10
  • Here is the link to question that relates to multiple value jQuery autocomplete - http://stackoverflow.com/questions/14784794/sorting-autocomplete-ui-results-based-on-match-location-multiple-values – Javier Brooklyn Feb 09 '13 at 03:58
  • can you answer that similar question http://stackoverflow.com/questions/38857001/autocomplete-search-for-a-specific-key-word\ – nouman arshad Aug 10 '16 at 14:47
  • @blim8183, your code doesn't work when the request term is not found, because indexOf will return -1, and therefore the term will always be at the beginning. So I changed it this way : `results = results.sort(function(a, b){ ` `var posa = a.produit.toLowerCase().indexOf(request.term.toLowerCase());` `var posb = b.produit.toLowerCase().indexOf(request.term.toLowerCase());` `if ( posa == -1 ) posa = 1000;` `if ( posb == -1 ) posb = 1000;` `return posa - posb;` `});` – Matthieu Nov 17 '17 at 07:30
  • Why are there commas instead of semicolons? It looks unreadable. – Meo Jun 10 '22 at 15:37
  • @Meo The commas aren't _instead of_ semicolons, the two are not interchangeable here. JS variables declared with `var` are function-scoped. Declaring all of your variables at the top of a function was typically a good practice 12 years ago when this answer was written. Using a single `var` statement within a function body was also common practice. This code is simply declaring and initializing multiple variables in a single `var` statement. – Andrew Whitaker Jun 12 '22 at 21:19
  • @AndrewWhitaker even jsfiddle warns about misleading linebreaks in your code. Imho this would look better http://jsfiddle.net/0k45r3ht/ – Meo Jun 16 '22 at 14:00
0

A possible optimization: cull items from the source list as they go into startsWith, and then you don't need to test for repetition when appending things that contain the search string. However, the trade-off is that the source array would need to be regenerated every time the input string changed.

Kent Rauch
  • 101
  • 1
0

It does seem to have problems when spaces are present in between words, please try the following as source

    source=[" Family And Community " , 
" Finance And Legal " , 
" Food And Beverages " , 
" Government " , 
" Health And Medicine " , 
" Home And Garden " , 
" Industrial Supplies And Services " ,
 " Non-governmental Organisations (ngos) " , 
" Personal Care " , 
" Public Utilities And Environment " , 
" Real-estate And Insurance " , 
" Religious Organisations And Associations " , 
" Shopping And Specialty Stores " , 
" Sports And Recreation " ,
 " Transportation " , 
" Travel And Tourism " , 
" Farming " , 
" Farming Equipments And Services " , 
" Feed, Seed And Grain " , 
" Fishing " , 
" Fishing Equipments And Services " , 
" Forests " , 
" Timber Equipments And Services " , 
" General Supplies And Services " , 
" Livestock " , 
" Wildlive " , 
" Minerals And Organic Matte " , 
" Accessories " , 
" Detailing And Aesthetics " , 
" Motorcycles " , 
" Motorised Vehicles " , 
" New And Used Dealers " , 
" Parts And Supplies " , 
" Related Services " , 
" Repairs Body Work " , 
" Repairs Mechanical " , 
" Trailers " , 
" Administrative And Specialty Services " , 
" Advertising " , 
" Associations - Organisations " , 
" Communication And Audio-visual " , 
" Consultants " , 
" Document Services " , 
" Employment And Career Resources " , 
" Engineers " , 
" Furniture And Office - Industrial Machines " , 
" Import And Export Services " , 
" Lawyers " , 
" Marketing And Sales " , 
" Office Supplies - General " , 
" Printing, Publishing And Copying " , 
" Shipping, Packaging And Postal Services " , 
" Trade Shows, Expositions And Conventions " , 
" Alterations And Services " , 
" Clothing - General " , 
" Clothes And Fashion Accessories " , 
" Footwear " , 
" Outerwear " , 
" Uniforms And Work Clothing " , 
" Communications Services And Equipments " , 
" Computer Equipments " , 
" Computer Services " , 
" Electronics - Services And Equipments " , 
" Information Systems " , 
" Internet - Communication And Events " , 
" Internet - Development And Services " , 
" Building Materials And Equipments " , 
" Ceramics And Tiles " , 
" Chimneys " , 
" Concrete, Cement And Paving " , 
" Contractor Equipments And Services " , 
" Design And Architecture " , 
" Electrical Products And Services " , 
" Floors, Ceilings And Roofs " , 
" Foundations And Piling " , 
" Hardware - Supplies And Services " , 
" Heavy Construction And Equipments " , 
" Inspectors And Surveyors " , 
" Painting And Plastering " , 
" Plumbing And Piping " , 
" Academic " , 
" Libraries " , 
" Services And Supplies " , 
" Specialised Schools "]