'Problem with filtering data by prefix and return collection of proper data with LINQ
I have a problem with doing filtering with LINQ. Should I use a Select method or it is a better way to do this task ? If yes, I would be grateful for every answer and explanation.
/// <summary> Filters a string sequence by a prefix value (case insensitive).
/// </summary>
/// <param name="data">Source string sequence.</param>
/// <param name="prefix">Prefix value to filter.</param>
/// <returns>
/// Returns items from data that started with required prefix (case insensitive).
/// </returns>
/// <exception cref="ArgumentNullException">Thrown when prefix is null.</exception>
/// <example>
/// { "aaa", "bbbb", "ccc", null }, prefix = "b" => { "bbbb" }
/// { "aaa", "bbbb", "ccc", null }, prefix = "B" => { "bbbb" }
/// { "a","b","c" }, prefix = "D" => { }
/// { "a","b","c" }, prefix = "" => { "a","b","c" }
/// { "horse","go","horse!" }, prefix = "horse" => { "horse","horse!" }
/// { "a","b","c", null }, prefix = "" => { "a","b","c" }
/// { "a","b","c" }, prefix = null => ArgumentNullException.
/// </example>
public IEnumerable<string> GetPrefixItems(IEnumerable<string>? data, string prefix)
{
if (prefix is null)
{
throw new ArgumentNullException(nameof(prefix));
}
return data!.Select(x => x.Contains(prefix,StringComparison.CurrentCultureIgnoreCase).ToString().ToLower(CultureInfo.CurrentCulture));
}
Solution 1:[1]
The correct answer depends on where your data is: is it in your local process (IEnumerable<string>), or is your data fetched by a different process, usually a Database Management System (IQueryable<string>).
The major difference is that there are no LINQ methods to control how your DBMS handles case sensitivity.
IEnumerable
Your sequence of strings is in your local process. All LINQ methods are supported, inclusive the extension methods that you define.
In this case it would be beter to change the procedure in an extension method. This way you can intertwine your procedure with any other LINQ methods. If you are not familiar with extension methods, read Extension Methods Demystified (10 minutes)
public static IEnumerable<string> WhereStartsWith( // TODO: invent a proper name
this IEnumerable<string> source,
string prefix)
{
// TODO: implement
}
Usage:
List<string> words = ...
IEnumerable<string> wordsStartingWithA = words.WhereStartsWith("A");
Or intertwined with other LINQ methods, so you can see the advantage of an extension method
IEnumerable<string> sortedLongWordsStartingWithA = words
.Where(word => word.Length > 10)
.WhereStartsWith("A")
.OrderBy(word => word);
By the way, my advice would be not to return null if your input sequence is null. If you do that you will have to check that the return value is not null before using other LINQ methods, like in the example above, before using method OrderBy.
Better have a pre-condition that the input sequence string should not be null, and if so, throw an exception, or decide to return an empty sequence.
The extension method is fairly simple:
public static IEnumerable<string> WhereStartsWith(
this IEnumerable<string> source,
string prefix)
{
return WhereStartsWith(source, prefix, null);
}
public static IEnumerable<string> WhereStartsWith(
this IEnumerable<string> source,
string prefix,
StringComparison comparison)
{
if (prefix == String.Empty)
{ // empty prefix: return all words in lower case
return source.Select(word => word.ToLower());
}
else
{ // non-empty prefix: return only words that start with prefix
if (comparison == null) comparison = StringComparison.CurrentCultureIgnoreCase;
return source.Where(word => word.StartsWith(prefix, comparison)
.Select(word => word.ToLower());
}
I choose to let the first extension method call the second one. This is the same structure you see with a lot of LINQ extension methods. Besides this enhances reusability.
Note that an exception is thrown if by Where if prefix is null. Therefore I don't have to check prefix for this.
By the way, you didn't specify if your prefix is case insensitive. Well, I guess by know you'll know what to change.
IQueryable
If your query is to be handled by a different process, like a Database Management System, then your input is IQueryable<string>. The problem is that usually the methods that you can use are limited. You can't add your own extension methods, nor can you call your own methods.
This is especially a problem for the case insensitivity. Quite often databases are case insensitive. If not, you'll have to do the SQL query yourself.
IQueryable<string> externalWords = ...
return externalWords.Where(word => word.StartsWith(prefix);
Most versions of entity framework know how to translate StartsWith into SQL.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |
