代码之家  ›  专栏  ›  技术社区  ›  Rich

如何在C中按搜索词筛选后按匹配数排序列表?

  •  0
  • Rich  · 技术社区  · 17 年前

    我的用户类中有一个过滤方法,它接受用户列表和一系列搜索词。当前,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;
        } );
    }
    

    我收到了一个新的要求,所以订购现在很重要。例如,在搜索“史密斯先生”时,亚当·史密斯先生应该先于夏娃·史密斯夫人,这可能会使我不恰当地使用contains。然而,最重要的是属性/部分术语匹配的数量。

    我想我可以有几个计数器来跟踪完全项匹配和部分匹配,然后按这两个计数器排序。我也愿意接受关于如何改进过滤方法的建议——也许完全使用其他方法。

    3 回复  |  直到 17 年前
        1
  •  4
  •   Jon Skeet    17 年前

    这是一个基于LINQ的解决方案。如果你不使用.NET 3.5,恐怕会更痛苦。为了清晰起见,它将匹配的细节与查询本身分开。

    您需要创建一个 LowerCaseUser 返回 User 对象的所有属性的大小写都较低-这样做一次比对每个搜索词都更有意义。如果你能把它和 UserMatches 进入 用户 上课,好多了。总之,这是密码。

    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;
    }
    
        2
  •  1
  •   Mikko Rantanen    17 年前

    第一件事,我要说的是,你需要做的,是打破大的懒惰评估或条件到单独的条件。否则你将永远无法解决你实际得到多少场比赛。在这之后,您可能需要为每个用户打分,以反映搜索词与之匹配的程度。

    我还假设您能够在这里使用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();
        }
    

    让这个方法返回一个评分客户也可以让你很容易地调整评分余额。比如说,你想要匹配的名字比匹配的公司更重要。

        3
  •  0
  •   Adam Robinson    17 年前

    完全匹配和部分匹配之间的区别是相关的,还是仅仅是标准的词典编纂排序?如果你给亚当·史密斯先生和夏娃·史密斯夫人分类,他们会按这个顺序排列。这只允许您使用标准排序lambda。

    推荐文章