0

I have a list of class Products:

class Products
{
    public string Name { get; set; }
    public string Size { get; set; }
    public string ProductId { get; set; }
    public string Category { get; set; }
}

I would like to use one TextBox to search through any matching products utilizing a wildcard value. This would return me a list of items where all values in the search string are found somewhere in the four properties listed above.

As of now, I'm using string[] values = searchText.Split("*".ToCharArray) to seperate the values of the search string into an array of strings (based on an asterisk wildcard). From there, I get stumped, since I want to search for all values of the search string in all properties of the class.

I tried to figure it out using a complex LINQ statement, but I have not been able to figure it out how to make this work. I don't know how to build a Where statement when I don't know how many values I'm going need to test against my four properties.

LiquidDrummer
  • 428
  • 1
  • 3
  • 14
  • 1
    When you say "wildcard search", what would the input look like? What wildcard characters do you want to support? Also, when you say, *"all values in the search string"*, do you mean they may have more than one term they're searching for? Some sample input and expected output may be helpful. – Rufus L May 22 '19 at 23:44
  • What I'd like is to be able to accept a search value like "hem*per" and that would return items where both "hem" and "per" could be found anywhere in the four values of a given Product object. This would return a product where the name could be "Hemerocallis" and the category could be "Perennial". – LiquidDrummer May 22 '19 at 23:48
  • @LiquidDrummer - So, based on that description, you're not doing a "wildcard" search. You're simply using a `*` as a delimiter to break up the individual strings you're looking for. – Enigmativity May 22 '19 at 23:54
  • @Enigmativity - Yes . . . sorry, the wildcard verbiage is a bit misleading. Sorry about that. – LiquidDrummer May 23 '19 at 00:06
  • @LiquidDrummer - Am I right that you just want `*` to delimit your keywords? – Enigmativity May 23 '19 at 00:08

2 Answers2

2

So, if you're breaking search up into separate keywords, using * as the delimiter, which you've described in the comments, then this is how you do it:

var products = new List<Products>()
{
    new Products()
    {
        Name = "theo frederick smith",
        Size = "",
        ProductId = "",
        Category = "brown",
    }
};

var searchText = "fred*brown";

var splits = searchText.Split("*".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);

var results =
    products
        .Where(p => splits.All(s =>
            p.Name.Contains(s)
            || p.Size.Contains(s)
            || p.ProductId.Contains(s)
            || p.Category.Contains(s)));

That matches the input.

Alternatively, if you really want a wildcard search, such as "fred*smith" (meaning that any one field must contain "fred" followed by zero or more characters and followed by "smith"), then this works:

var products = new List<Products>()
{
    new Products()
    {
        Name = "theo frederick smith",
        Size = "",
        ProductId = "",
        Category = "brown",
    }
};

var searchText = "fred*smith";

var wildcard =
    new Regex(
        String.Join(".*",
            searchText
                .Split('*')
                .Select(x => Regex.Escape(x))));

var results =
    products
        .Where(p => new []
        {
            p.Name, p.Size, p.ProductId, p.Category
        }.Any(x => wildcard.IsMatch(x)));
Enigmativity
  • 113,464
  • 11
  • 89
  • 172
1

Naively, you could write

products.Where(x=>x.Name.Contains(search) 
                  || x.Size.Contains(search) 
                  || x.ProductId.Contains(search) 
                  || x.Category.Contains(search))

You would be better off putting that logic in your Product class.

So you would have:

class Products
{
    public bool Contains(string term) {
              return Name.Contains(search) || Size.Contains(search) || 
              ProductId.Contains(search) || Category.Contains(search)
    }

    public string Name { get; set; }
    public string Size { get; set; }
    public string ProductId { get; set; }
    public string Category { get; set; }
}

And then simply products.Where(x=>x.Contains(search))

You could also use reflection to get all the property names and do a for each on each string and check for Contains.

Matt
  • 25,943
  • 66
  • 198
  • 303
  • How does this handle the "wildcard" requirement? – Enigmativity May 23 '19 at 00:06
  • It doesn't, when I wrote it, it just showed as delimited on a wildcard - so in that case, the author would have had to loop through each term until one was found. – Matt May 23 '19 at 00:08