簡體   English   中英

在C#中按搜索字詞過濾后,如何按匹配數排序列表?

[英]How can I order a list by number of matches after filtering by Search Terms in C#?

我的用戶類中有一個篩選器方法,該方法接受一個用戶列表和一串搜索詞。 當前,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方法的建議持開放態度-也許完全使用其他方法。

這是基於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;
}

我要說的第一件事,是將懶惰的大型評估或條件分解為單獨的條件。 否則,您將永遠無法解決您實際獲得的匹配數。 之后,您可能需要為每個用戶評分,以反映搜索詞與之匹配的程度。

我還假設您已經在使用lambda表達式,因此您可以在這里使用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();
    }

讓該方法返回一個計分的客戶也使您以后可以輕松地調整得分余額。 假設您希望匹配的名字比匹配的公司更重要。

完全匹配和部分匹配之間的區別是相關的,還是僅僅是標准的字典分類? 如果要對亞當·史密斯先生和夏娃·史密斯女士進行排序,他們將按照該順序排列。 那將只允許您使用標准的訂購lambda。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM