HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpMinor

Search for variants of keywords using LINQ

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
linqsearchvariantsusingkeywordsfor

Problem

First, I'm adding words entered into a search field to an array. Then, I'm getting results from a stored procedure on SQL Server using Database First approach with Entity Framework.

Finally, for each word in the array, I'm filtering the results using this query:

```
products = products.Where(item => (item.basedescription.ToLower().StartsWith(searchWords[x] + " ")
|| item.basedescription.ToLower().StartsWith(service.Pluralize(searchWords[x]) + " ")
|| item.basedescription.ToLower().StartsWith(service.Singularize(searchWords[x]) + " ")
|| item.basedescription.ToLower().EndsWith(" " + searchWords[x])
|| item.basedescription.ToLower().EndsWith(" " + service.Pluralize(searchWords[x]))
|| item.basedescription.ToLower().EndsWith(" " + service.Singularize(searchWords[x]))
|| item.basedescription.ToLower().Contains(" " + searchWords[x] + " ")
|| item.basedescription.ToLower().Contains(" " + service.Pluralize(searchWords[x]) + " ")
|| item.basedescription.ToLower().Contains(" " + service.Singularize(searchWords[x]) + " ")
|| item.basedescription.ToLower().Contains(searchWords[x])
|| item.basedescription.ToLower().Contains(service.Pluralize(searchWords[x]))
|| item.basedescription.ToLower().Contains(service.Singularize(searchWords[x])))

|| (item.info.ToLower().StartsWith(searchWords[x] + " ")
|| item.info.ToLower().StartsWith(service.Pluralize(searchWords[x]) + " ")
|| item.info.ToLower().StartsWith(service.Singularize(searchWords[x]) + " ")
|| item.info.ToLower().EndsWith(" " + searchWords[x])
|| item.info.ToLower().EndsWith(" " + service.Pluralize(searchWords[x]))
|| item.info.ToLower().EndsWith(" " + service.Singularize

Solution

It seems weird or unusual that you're assigning products from products. products is your IQueryable source, and the result of the query is some List.

That said, this is one such case where the query syntax has an advantage over the method syntax - be it only to reduce the repetition by leveraging the let keyword and caching the service method calls' return values instead of re-computing them for every single comparison, for every single item in your source:

var value = searchWords[x];
var pluralized = service.Pluralize(value);
var singularized = service.Singularized(value);

var result = (from product in products
    let description = product.basedescription.ToLower()
    let info = product.info.ToLower()
    let itemGroup = product.itemgroup
    where description.StartsWith(pluralized)
       || description.StartsWith(singularized)
       || ...
       || info.StartsWith(pluralized)
       || info.StartsWith(singularized)
       || ...
       || itemGroup.StartsWith(pluralized)
       || itemGroup.StartsWith(singularized)
       || ...
    ).ToList();


The || operator is short-circuiting, so the whole expression is true as soon as one condition is true - by putting the .Contains checks first, the .StartsWith and .EndsWith checks would only be executed when .Contains is false... but then, these checks are redundant:

  • If .Contains(value) is true...



  • .Contains(" " + value + " ") is true.



  • .StartsWith(value) is also true.



  • .EndsWith(value) is true as well.



If .Contains(value) is false, then all of the above are false as well.

Consider extracting each "group" of conditions into their own, well-named, private function.

Remember that in C#, member names should be PascalCase, so basedescription should be BaseDescription, for example.

The final code could look like this:

var value = searchWords[x];
var pluralized = service.Pluralize(value);
var singularized = service.Singularized(value);

var result = (from product in products
    let description = product.basedescription.ToLower()
    let info = product.info.ToLower()
    let itemGroup = product.itemgroup.ToLower()
    where IsFound(value, description, pluralized, singularized)
       || IsFound(value, info, pluralized, singularized)
       || IsFound(value, itemGroup, pluralized, singularized)
    ).ToList();


Where IsFound is a little function that matches a Func delegate.

string.ToLower() and string.ToLowerInvariant() is probably a relevant read, too... as would be How can I do a case insensitive string comparison.

Code Snippets

var value = searchWords[x];
var pluralized = service.Pluralize(value);
var singularized = service.Singularized(value);

var result = (from product in products
    let description = product.basedescription.ToLower()
    let info = product.info.ToLower()
    let itemGroup = product.itemgroup
    where description.StartsWith(pluralized)
       || description.StartsWith(singularized)
       || ...
       || info.StartsWith(pluralized)
       || info.StartsWith(singularized)
       || ...
       || itemGroup.StartsWith(pluralized)
       || itemGroup.StartsWith(singularized)
       || ...
    ).ToList();
var value = searchWords[x];
var pluralized = service.Pluralize(value);
var singularized = service.Singularized(value);

var result = (from product in products
    let description = product.basedescription.ToLower()
    let info = product.info.ToLower()
    let itemGroup = product.itemgroup.ToLower()
    where IsFound(value, description, pluralized, singularized)
       || IsFound(value, info, pluralized, singularized)
       || IsFound(value, itemGroup, pluralized, singularized)
    ).ToList();

Context

StackExchange Code Review Q#140248, answer score: 4

Revisions (0)

No revisions yet.