LINQ OrderBy: лучшие результаты поиска вверху списка результатов - PullRequest
2 голосов
/ 19 мая 2010

Рассмотрим необходимость поиска в списке Заказчиков как по имени, так и по фамилии. Желание состоит в том, чтобы Заказчик сортировал список результатов с наибольшим количеством совпадений в поисковых запросах.

FirstName      LastName
----------     ---------
Foo            Laurie
Bar            Jackson
Jackson        Bro
Laurie         Foo
Jackson        Laurie
string[] searchTerms = new string[] {"Jackson", "Laurie"};

//want to find those customers with first, last or BOTH names in the searchTerms
var matchingCusts = Customers
                       .Where(m => searchTerms.Contains(m.FirstName)
                               ||  searchTerms.Contains(m.LastName))
                       .ToList();

/* Want to sort for those results with BOTH FirstName and LastName 
   matching in the search terms. Those that match on both First and Last
   should  be at the top of the results, the rest who match on 
   one property should be below.
*/

 return matchingCusts.OrderBy(m=>m); 

Желаемая сортировка:

Jackson        Laurie  (matches on both properties)
Foo            Laurie
Bar            Jackson
Jackson        Bro
Laurie         Foo

Как мне добиться этой желаемой функциональности с помощью LINQ и OrderBy / OrderByDescending?

Ответы [ 3 ]

10 голосов
/ 19 мая 2010

Используйте Select, чтобы спроецировать «оценку соответствия» с клиентом, а затем заказать:

class Program
{
    static void Main(string[] args)
    {
        var Customers = new Customer[]
        {
            new Customer { FirstName = "Foo", LastName = "Laurie" },
            new Customer { FirstName = "Bar", LastName = "Jackson" },
            new Customer { FirstName = "Jackson", LastName = "Bro" },
            new Customer { FirstName = "Laurie", LastName = "Foo" },
            new Customer { FirstName = "Jackson", LastName = "Laurie" },
        };

        string[] searchTerms = new string[] { "Jackson", "Laurie" };

        //want to find those customers with first, last or BOTH names in the searchTerms 
        var matchingCusts = Customers
                               .Where(m => searchTerms.Contains(m.FirstName)
                                       || searchTerms.Contains(m.LastName))
                               .ToList();

        var result = matchingCusts.Select(x => new
            {
                Customer = x,
                MatchEvaluation = (searchTerms.Contains(x.FirstName) ? 1 : 0) + (searchTerms.Contains(x.LastName) ? 1 : 0),
            })
            .OrderByDescending(x => x.MatchEvaluation)
            .Select(x => x.Customer);

        foreach (var c in result)
        {
            Console.WriteLine(c.FirstName + " " + c.LastName);
        }

        Console.ReadKey();
    }

    public sealed class Customer
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
    }
}
5 голосов
/ 19 мая 2010

Так как я полностью пропустил оригинальный вопрос:

Один лайнер:)

var matchingCusts = Customers
    .Where(m => searchTerms.Contains(m.FirstName) ||
                searchTerms.Contains(m.LastName))
    .Select(c => new
    {
        Customer = c,
        Sort = ((searchTerms.Contains(c.FirstName) ? 0 : 1) + 
                (searchTerms.Contains(c.LastName) ? 0 : 1))
    })
    .OrderBy(o => o.Sort)
    .ThenBy(o1 => o1.Customer.LastName)
    .ThenBy(o2 => o2.Customer.FirstName)
    .Select(r => r.Customer);
0 голосов
/ 19 мая 2010

Вы можете сделать свой собственный IEqualityComparer<Customer> для перехода в функции orderby.

Заставить конструктор взять список searchTerms, а затем расставить приоритеты над алфавитным.

Тогда делай .SortBy(c => c.LastName, new MyEqualityComparer(searchTerms)).ThenBy(c => c.FirstName, new MyEqualityComparer(searchTerms))

...