对集合进行排序并根据特定条件对结果进行排名
说我有以下内容
var searches = new ObservableCollection();
searches
包含书籍对象
public class Book { public string Title { get; set;} public string Desc {get; set;} }
我想通过匹配的字符串对searches
进行排序。 首先,它检查Title
不是根据Title
开头的搜索字符串的接近程度对它们进行排名。 接下来,它检查Desc
并根据搜索字符串从描述的开头出现的接近程度对它们进行排名。
例如,如果我有
书1
标题:ABC书名
描述:书1的描述书2
标题:仅限书名
说明:第2册的描述中有一个ABC书3
标题:书名ABC
描述:ABC在一开始
所以,假设搜索关键字是ABC
,我希望对searches
进行排序,以便获得以下内容。 结果将更高优先级放在标题中包含搜索字符串的项目中。
书1
标题:ABC书名
描述:书1的描述书3
标题:书名ABC
描述:ABC在一开始书2
标题:仅限书名
说明:第2册的描述中有一个ABC
我如何使用LINQ实现这一目标?
您可以使用排名function为每本书定义“分数”,然后按分数排序。
即
var searchString = "ABC"; var results = books.Select(b => new { Book = b, Rank = RankBook(b, searchString) }) .OrderBy(r => r.Rank) .Select(r => r.Book.Title);
并且排名function:
private int RankBook(Book b, string searchString) { int rank = 0; if (b.Title.Contains(searchString)) rank += 10; if (b.Desc.Contains(searchString)) rank += 5; return rank; }
这就是说:发现在title = 10分,发现在desc = 5分,所以你得到最高分的最相关的书。
您可以使用OrderBy和ThenBy
var searches = new ObservableCollection(); searches.Add(new Book() { Desc = "The description of book 1", Title = "ABC Book Title" }); searches.Add(new Book() { Desc = "Book Title Only", Title = "There's an ABC in the description of book 2" }); searches.Add(new Book() { Desc = "Book Title ABC", Title = "ABC is in the beginning" }); var ordered = new ObservableCollection (searches.OrderBy(book => book.Title).ThenBy(book => book.Desc.Contains("ABC")));
更新
我添加了一个排名系统,希望能帮助您找到所需的内容。 我使用的只是IndexOf来确定条件的位置并将其存储在Book对象的属性中。 我的另一个建议是你为你的书创建一个独立的集合(使用inheritance),这样你就可以根据需要自定义它,而不必在对象本身的上下文之外编写太多代码
public class BookCollection : ObservableCollection // Notice the Inheritance to ObservableCollection { public void SetCriteria(string search) { if(string.IsNullOrEmpty(search)) return; foreach (var book in this) { if(book.Title.Contains(search)) book.TitleRank = book.Title.IndexOf(search, StringComparison.InvariantCulture); if(book.Desc.Contains(search)) book.DescRank = book.Desc.IndexOf(search, StringComparison.InvariantCulture); } var collection = new List (base.Items.OrderBy(book => book.Title) .ThenBy(book => book.Desc) .ThenBy(book => book.TitleRank) .ThenBy(book => book.DescRank)); Items.Clear(); collection.ForEach(Add); collection.Clear(); } } public class Book { public string Title { get; set; } public string Desc { get; set; } public int TitleRank { get; internal set; } public int DescRank { get; internal set; } }
现在要使用这个新的集合,你所要做的就是这样称呼它。
var collection = new BookCollection(); collection.Add(new Book { Desc = "Book Title ABC", Title = "ABC is in the beginning" }); // Add your other books here........ collection.SetCriteria("ABC"); // your new collection is now sorted and ready to use, no need to write any extra sorting code here
请记住,如果您需要为排序添加更多条件,则必须在SetCriteria方法中执行此操作。 希望这可以帮助。
感谢@ Patel和Stefano的建议,我得出了以下解决方案
var sorted = searches.Select(tile => new { TileViewModel = tile, Rank = rankResult(tile, text) }) .OrderByDescending(r => r.Rank) .Select(r => r.TileViewModel); SearchResultsTilesVM = new ObservableCollection(sorted);
获取关键字位置的方法。 如果在标题中找到匹配项,我会添加额外的点数。
private int rankResult(TileViewModel vm, string keyword) { double rank = 0; //Added 100 to give stronger weight when keyword found in title int index = vm.Title.IndexOf(keyword, StringComparison.InvariantCultureIgnoreCase); if (index >= 0 ) { rank = (double)(vm.Title.Length - index) / (double)vm.Title.Length * 100 + 100; } int index2 = vm.Information.IndexOf(keyword, StringComparison.InvariantCultureIgnoreCase); if (index2 >= 0) { rank += (double)(vm.Information.Length - index2) / (double)vm.Information.Length * 100; } return Convert.ToInt32(rank); }