[英]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.