说我有以下内容
var searches = new ObservableCollection<Book>();
Run Code Online (Sandbox Code Playgroud)
searches 包含书籍对象
public class Book
{
public string Title { get; set;}
public string Desc {get; set;}
}
Run Code Online (Sandbox Code Playgroud)
我想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实现这一目标?
您可以使用排名功能为每本书定义"分数",然后按分数排序.
即
var searchString = "ABC";
var results = books.Select(b => new { Book = b, Rank = RankBook(b, searchString) })
.OrderBy(r => r.Rank)
.Select(r => r.Book.Title);
Run Code Online (Sandbox Code Playgroud)
并且排名功能:
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;
}
Run Code Online (Sandbox Code Playgroud)
这就是说:在标题= 10点,在desc = 5分中找到,所以你得到最高分的最相关的书.
您可以使用OrderBy和ThenBy
var searches = new ObservableCollection<Book>();
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<Book>(searches.OrderBy(book => book.Title).ThenBy(book => book.Desc.Contains("ABC")));
Run Code Online (Sandbox Code Playgroud)
我已经添加了一个排名系统,希望能帮助您找到您想要的东西.我使用的只是IndexOf来确定条件的位置并将其存储在Book对象的属性中.我的另一个建议是你为你的书创建一个独立的集合(使用继承),这样你就可以根据需要自定义它,而不必在对象本身的上下文之外编写太多代码
public class BookCollection : ObservableCollection<Book> // 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<Book>(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; }
}
Run Code Online (Sandbox Code Playgroud)
现在要使用这个新的集合,你所要做的就是这样称呼它.
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
Run Code Online (Sandbox Code Playgroud)
请记住,如果您需要为排序添加更多条件,则必须在SetCriteria方法中执行此操作.希望这可以帮助.
感谢 @M 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<TileViewModel>(sorted);
Run Code Online (Sandbox Code Playgroud)
取关键字位置的方法。如果标题中找到匹配项,我会加分。
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);
}
Run Code Online (Sandbox Code Playgroud)