Как я могу заказать список по количеству совпадений после фильтрации по условиям поиска в C #? - PullRequest
0 голосов
/ 24 апреля 2009

У меня есть метод фильтра в моем классе пользователей, который принимает список пользователей и строку поисковых запросов. В настоящее время предикат FindAll разделяет термины по пробелам, а затем возвращает совпадение, если какое-либо из доступных для поиска свойств содержит какую-либо часть терминов.

public static List<User> FilterBySearchTerms( List<User> usersToFilter, string searchTerms, bool searchEmailText )
{
    return usersToFilter.FindAll( user =>
    {
        // Convert to lower case for better comparison, trim white space and then split on spaces to search for all terms
        string[] terms = searchTerms.ToLower().Trim().Split( ' ' );

        foreach ( string term in terms )
        {
            // TODO: Is this any quicker than two separate ifs?
            if ( 
                    (searchEmailText && user.Email.ToLower().Contains( term )) 
                    || (
                        user.FirstName.ToLower().Contains( term ) || user.Surname.ToLower().Contains( term ) 
                        || user.Position.ToLower().Contains( term ) || user.Company.ToLower().Contains( term ) 
                        || user.Office.ToLower().Contains( term ) 
                        || user.Title.ToLower().Contains( term )
                    )
            )
                return true;
            // Search UserID by encoded UserInviteID
            else 
            {
                int encodedID;
                if ( int.TryParse( term, out encodedID ) )
                {
                    User fromInvite = GetByEncodedUserInviteID( encodedID );
                    if ( fromInvite != null && fromInvite.ID.HasValue && fromInvite.ID.Value == user.ID )
                        return true;
                }
            }
        }

        return false;
    } );
}

Я получил новое требование, так что заказ теперь важен. Например, поиск «Мистер Смит» должен опередить мистера Адама Смита перед миссис Ив Смит, что может сделать мое использование «Содержит» неуместным. Тем не менее, самое важное - это количество свойств / часть совпадений терминов.

Я думаю, у меня может быть пара счетчиков, чтобы отслеживать полные и частичные совпадения, а затем упорядочить по этим двум. Я также открыт для предложений о том, как можно улучшить метод Filter - возможно, используя что-то еще полностью.

Ответы [ 3 ]

4 голосов
/ 24 апреля 2009

Вот решение на основе LINQ. Боюсь, будет немного больше боли, если вы не используете .NET 3.5. Для ясности он отделяет детали соответствия от самого запроса.

Вам потребуется создать метод LowerCaseUser, который возвращает объект User со всеми свойствами в нижнем регистре - более разумно сделать это один раз, чем для каждого поискового запроса. Если вы можете поместить это и UserMatches в класс User, тем лучше. Во всяком случае, вот код.

public static List<User> FilterBySearchTerms
    (List<User> usersToFilter, 
     string searchTerms,
     bool searchEmailText)
{
    // Just split the search terms once, rather than for each user
    string[] terms = searchTerms.ToLower().Trim().Split(' ');

    return (from user in usersToFilter
            let lowerUser = LowerCaseUser(user)
            let matchCount = terms.Count(term => 
                                         UserMatches(lowerUser, term))
            where matchCount != 0
            orderby matchCount descending
            select user).ToList();
}

private static bool UserMatches(User user, string term,
                                bool searchEmailText)
{
    if ((searchEmailText && user.Email.Contains(term))
        || user.FirstName.Contains(term)
        || user.Surname.Contains(term)
        || user.Position.Contains(term)
        || user.Company.Contains(term)
        || user.Office.Contains(term)
        || user.Title.Contains(term))
    {
        return true;
    }
    int encodedID;
    if (int.TryParse(term, out encodedID))
    {
        User fromInvite = GetByEncodedUserInviteID(encodedID);
        // Let the compiler handle the null/non-null comparison
        if (fromInvite != null && fromInvite.ID == user.ID)
        {
            return true;
        }
    }
    return false;
}
1 голос
/ 24 апреля 2009

Во-первых, я бы сказал, что вам нужно сделать, это разбить большие ленивые оценки или условия на отдельные условия. В противном случае вы никогда не решите, сколько совпадений вы получите. После этого вам, вероятно, понадобится оценка для каждого пользователя, которая отражает, насколько хорошо ему соответствуют условия поиска.

Я также предполагаю, что вы можете использовать LINQ здесь, поскольку вы уже используете лямбда-выражения.

    class ScoredUser
    {
        public User User { get; set; }
        public int Score { get; set; }
    }

    public static List<User> FilterBySearchTerms(List<User> usersToFilter, string searchTerms, bool searchEmailText)
    {
        // Convert to lower case for better comparison, trim white space and then split on spaces to search for all terms
        string[] terms = searchTerms.ToLower().Trim().Split(' ');

        // Run a select statement to user list which converts them to
        // a scored object.
        return usersToFilter.Select(user =>
        {
            ScoredUser scoredUser = new ScoredUser()
            {
                User = user,
                Score = 0
            };

            foreach (string term in terms)
            {
                if (searchEmailText && user.Email.ToLower().Contains(term))
                    scoredUser.Score++;

                if (user.FirstName.ToLower().Contains(term))
                    scoredUser.Score++;

                if (user.Surname.ToLower().Contains(term))
                    scoredUser.Score++;

                // etc.
            }

            return scoredUser;

            // Select all scored users with score greater than 0, order by score and select the users.
        }).Where(su => su.Score > 0).OrderByDescending(su => su.Score).Select(su => su.User).ToList();
    }

Наличие метода возврата забитого клиента также позволяет в дальнейшем легко настраивать баллы оценки. Скажем, вы хотите, чтобы подходящее имя имело большее значение, чем, например, подходящая компания.

0 голосов
/ 24 апреля 2009

Является ли различие между полным и частичным соответствием релевантным или просто стандартной лексикографической сортировкой? Если бы вы отсортировали мистера Адама Смита и миссис Ив Смит, они были бы расположены в таком порядке. Это позволило бы вам использовать стандартную лямбду для заказа.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...