user1219694
user1219694

Reputation: 209

LINQ Sort by Matching Character Position

I am implementing a “Search” autocomplete. I’d like to return a list of results sorted by the ordering of the submitted characters by the user where items at the top of the sorted results are those items that have the characters in the string in the first to n characters.

Is there an elegant way to do this using LINQ?

For example, assume that when the user types “un” the following items are returned from the database.

I would like to return the ordered result for display as follows:

  1. United States
  2. Texas, United States
  3. Florida, United States
  4. New York, United States

Notice that for each of the strings above “un” is in position 1-2, 7-8, 10-11 and 11-12.

Upvotes: 3

Views: 2188

Answers (4)

George Mamaladze
George Mamaladze

Reputation: 7921

Generally the answer from @HugoRune will work, but it's a bit inefficient having two issues.

  1. Using ToLower() leads all strings to converted first to lowercase, even if the comparison of firts char shows that they do not match.

  2. We perform basically the same comparison two times. Once with Contains() and once with IndexOf(). So we run through string two times.

Following code is at least twice as efficient:

        IEnumerable<string> stateNames= [your source here];
        string searchString="un";

        var result = 
            stateNames
                .Select(state=> new {Name=state, Index = state.IndexOf(searchString, StringComparison.CurrentCultureIgnoreCase)})
                .Where(tuple=>tuple.Index>=0)
                .OrderBy(tuple=>tuple.Index)
                .Select(tuple=>tuple.Name);

Upvotes: 3

HatSoft
HatSoft

Reputation: 11191

My suggestions for implementing auto-complete functionality would be to get all country names and their id ordered by name in ascending order at application load and cache. Now whenever you user types it should use this cache resultset for match using linq query. Another tweak to gain better performance will be to have minimum characters e.g. 2 in your case then only do linq query

Reasons to implement this way : You will hit the database just once to get all country names instead of keyup on every character. Mind you country changes are not done everyday so your cache can expire only on application restart.

Hope this helps

Upvotes: 0

Josh E
Josh E

Reputation: 7424

Use CompareTo method like so:

SomeContext.ACollection.Where(...).OrderBy(x=> searchTerm.CompareTo(x) > 0)

The CompareTo method returns an integer signifying where the compared strings would be sorted in character order.

Upvotes: 0

HugoRune
HugoRune

Reputation: 13789

You can get the position of a substring with IndexOf, and you can use that index in a OrderBy clause

(the following assumes you have a db table "States" with a column "Name")

var result = dc.States
             .Where(s=>s.Name.ToLower().Contains("un"))
             .OrderBy(s=>s.Name.ToLower().IndexOf("un"));

or a simple caching solution, if you do not want to hit the db each time:

// call this once during initialisation
List<States> cachedStates = 
    dc.States.ToList();

...

// call this every time
var result = cachedStates
             .Where(s=>s.Name.ToLower().Contains("un"))
             .OrderBy(s=>s.Name.ToLower().IndexOf("un"));

Upvotes: 1

Related Questions