【问题标题】:How can I order a list by number of matches after filtering by Search Terms in C#?在 C# 中按搜索词过滤后,如何按匹配数对列表进行排序?
【发布时间】:2010-10-21 11:15:27
【问题描述】:

我的用户类中有一个过滤器方法,它接收一个用户列表和一串搜索词。目前,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 方法的建议——也许完全使用其他方法。

【问题讨论】:

    标签: c# search list filter


    【解决方案1】:

    这是一个基于 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;
    }
    

    【讨论】:

    • 您是否会在 orderby matchCount 之后为用户名添加辅助 orderby 以保持字母顺序?
    • 可能,如果需要的话。那里有各种各样的选择:)
    • 您好乔恩,感谢您的回复。我从你的代码中得到了几个错误。 1. System.Linq.Enumerable.Count(System.Collections.Generic.IEnumerable, System.Func)' 方法的类型参数不能从用法中推断出来。尝试明确指定类型参数。 2.当前上下文中不存在名称'term'我恐怕之前没有使用过Linq所以不知道它是如何工作的......
    • 将 let matchcount 行更改为此修复它: let matchCount = terms.Count( term => UserMatches( lowerUser, term, searchEmailText ) )
    • 不错。我喜欢LowerCaseUser(user),它只为同一个字符串制作一次ToLower。如果将术语(所有术语)作为 UserMatches 的参数,则可以在找到第一个时停止搜索。 (您不需要全部计数)。但它给你带来了代码中的另一个循环。
    【解决方案2】:

    我想说你需要做的第一件事是将大型惰性评估或条件分解为单独的条件。否则,您将永远无法解决实际获得的匹配数量。在此之后,您可能需要为每个用户打分,以反映搜索词与其匹配的程度。

    我还假设您可以在此处使用 LINQ,因为您已经在使用 lambda 表达式。

        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();
        }
    

    让该方法返回评分客户还可以让您稍后轻松调整评分余额。例如,假设您想要匹配的名字比匹配的公司更重要。

    【讨论】:

    • .. 当然,我完全把自己与不同的术语和不同的领域混淆了。因此,不需要打破惰性评估,尽管它确实允许在不同的比赛中获得不同的分数。在这种情况下,可能应该与“else if”结合使用。
    【解决方案3】:

    完全匹配和部分匹配之间的区别是相关的,还是只是标准的字典排序?如果您要对 Adam Smith 先生和 Eve Smith 夫人进行排序,它们将按此顺序排列。这只允许您使用标准排序 lambda。

    【讨论】:

    • 字典顺序是次要的,仅次于匹配的数量。
    猜你喜欢
    • 2011-05-23
    • 1970-01-01
    • 2018-02-20
    • 1970-01-01
    • 1970-01-01
    • 2011-07-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多