The following search method works fine for up to two terms.
How can I make it dynamic so that it is able to handle any number of search terms?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace TestContains82343
{
class Program
{
static void Main(string[] args)
{
List<string> tasks = new List<string>();
tasks.Add("Add contract to Customer.");
tasks.Add("New contract for customer.");
tasks.Add("Create new contract.");
tasks.Add("Go through the old contracts.");
tasks.Add("Attach files to customers.");
var filteredTasks =开发者_如何学C SearchListWithSearchPhrase(tasks, "contract customer");
filteredTasks.ForEach(t => Console.WriteLine(t));
Console.ReadLine();
}
public static List<string> SearchListWithSearchPhrase(List<string> tasks, string searchPhrase)
{
string[] parts = searchPhrase.Split(new char[] { ' ' });
List<string> searchTerms = new List<string>();
foreach (string part in parts)
{
searchTerms.Add(part.Trim());
}
switch (searchTerms.Count())
{
case 1:
return (from t in tasks
where t.ToUpper().Contains(searchTerms[0].ToUpper())
select t).ToList();
case 2:
return (from t in tasks
where t.ToUpper().Contains(searchTerms[0].ToUpper()) && t.ToUpper().Contains(searchTerms[1].ToUpper())
select t).ToList();
default:
return null;
}
}
}
}
How about replacing
switch (searchTerms.Count())
{
case 1:
return (from t in tasks
where t.ToUpper().Contains(searchTerms[0].ToUpper())
select t).ToList();
case 2:
return (from t in tasks
where t.ToUpper().Contains(searchTerms[0].ToUpper()) && t.ToUpper().Contains(searchTerms[1].ToUpper())
select t).ToList();
default:
return null;
}
By
(from t in tasks
where searchTerms.All(term => t.ToUpper().Contains(term.ToUpper()))
select t).ToList();
Just call Where
repeatedly... I've changed the handling of searchTerms
as well to make this slightly more LINQ-y :)
public static List<string> SearchListWithSearchPhrase
(List<string> tasks, string searchPhrase)
{
IEnumerable<string> searchTerms = searchPhrase.Split(' ')
.Select(x => x.Trim());
IEnumerable<string> query = tasks;
foreach (string term in searchTerms)
{
// See edit below
String captured = term;
query = query.Where(t => t.ToUpper().Contains(captured));
}
return query.ToList();
}
You should note that by default, ToUpper()
will be culture-sensitive - there are various caveats about case-insensitive matching :( Have a look at this guidance on MSDN for more details. I'm not sure how much support there is for case-insensitive Contains
though :(
EDIT: I like konamiman's answer, although it looks like it's splitting somewhat differently to your original code. All
is definitely a useful LINQ operator to know about...
Here's how I would write it though:
return tasks.Where(t => searchTerms.All(term => t.ToUpper().Contains(term)))
.ToList();
(I don't generally use a query expression when it's a single operator applied to the outer query.)
EDIT: Aargh, I can't believe I fell into the captured variable issue :( You need to create a copy of the foreach
loop variable as otherwise the closure will always refer to the "current" value of the variable... which will always be the last value by the time ToList
is executed :(
EDIT: Note that everything so far is inefficient in terms of uppercasing each task several times. That's probably fine in reality, but you could avoid it by using something like this:
IEnumerable<string> query = tasks.Select
(t => new { Original = t, Upper = t.ToUpper });
return query.Where(task => searchTerms.All(term => task.Upper.Contains(term)))
.Select(task => task.Original)
.ToList();
Can't test code right now, but you could do something similar to this:
from t in tasks
let taskWords=t.ToUpper().Split(new char[] { ' ' });
where searchTerms.All(term => taskWords.Contains(term.ToUpper()))
select t
Replace the switch statement with a for loop :)
[TestMethod]
public void TestSearch()
{
List<string> tasks = new List<string>
{
"Add contract to Customer.",
"New contract for customer.",
"Create new contract.",
"Go through the old contracts.",
"Attach files to customers."
};
var filteredTasks = SearchListWithSearchPhrase(tasks, "contract customer new");
filteredTasks.ForEach(Console.WriteLine);
}
public static List<string> SearchListWithSearchPhrase(List<string> tasks, string searchPhrase)
{
var query = tasks.AsEnumerable();
foreach (var term in searchPhrase.Split(new[] { ' ' }))
{
string s = term.Trim();
query = query.Where(x => x.IndexOf(s, StringComparison.InvariantCultureIgnoreCase) != -1);
}
return query.ToList();
}
why not use a foreach and AddRange() after splitting the terms and saving it into a list.
List<ItemsImLookingFor> items = new List<ItemsImLookingFor>();
// search for terms
foreach(string term in searchTerms)
{
// add users to list
items.AddRange(dbOrList(
item => item.Name.ToLower().Contains(str)).ToList()
);
}
that should work for any amount of terms.
精彩评论