Multiple like in lambda expression - c#

I have a string[] variable which i want to use in the lambda expression to filter data using like clause. I tried using contains, but it works like the 'in' clause, which is not what I need.
Please see the code below which behaves like in clause:
var inventories = ..............AsQuerable();
string[] strArray = <<somevalues>>;
inventories = inventories.Where(c => !strArray.Contains(c.columnName));
Could someone provide me the lambda expression which could filter records using like instead of in using an array.

The only methods LINQ provide for this purpose is .Where(), .StartsWith() or .EndsWith().. Also, there is pretty similar question here How to do SQL Like % in Linq?

Unlike VB.NET, C# has no builtin like-operator. However, you can use the operator from VB.NET easily. What you have to do is to reference the assembly Microsoft.VisualBasic.dll and include a using Microsoft.VisualBasic.CompilerServices; at the top of your file. Then you can do
var inventories = ..............AsQuerable();
string[] strArray = <<somevalues>>;
inventories = inventories.Where(c => !strArray.Any(s => LikeOperator.LikeString(c.columnName, s, CompareMethod.Text)));

I'm not entirely sure what you are trying to do, but I think this is what you are trying to accomplish. If not, please provide some more information and I will update my answer.
To check if a string is equal to a part of another string (which like does), you can use the .Contains method:
bool contains = "Some fancy sentence".Contains("fancy");
This will evaluate to true. Given your example, this would result in the following:
var inventories = ..............AsQuerable();
string[] strArray = <<somevalues>>;
inventories = inventories.Where(inv => !strArray.Any(s => inv.columnName.Contains(s)));
This checks all inventories and removes all inventories where the inventory column name (partially) occurs in any of the strArray values.

It is not pure Lambda expression, but Maybe it can help:
List<SomeObject> inventories = new List<SomeObject>();
// add objects to list...
string[] strArray = <<somevalues>>;
// result will be stored here
List<SomeObject> filtered = new List<SomeObject>();
foreach (var itm in strArray)
{
// LIKE '%SOMEVALUE%'
var match = inventories.Where(x => x.columnName.Contains(itm)).ToList();
// LIKE '%SOMEVALUE'
// var match = inventories.Where(x => x.columnName.StartsWith(itm)).ToList();
// LIKE 'SOMEVALUE%'
// var match = inventories.Where(x => x.columnName.EndsWith(itm)).ToList();
foreach (var m in match)
filtered.Add(m);
}

Related

Using a dictionary as a source of Regex.Replace patterns in a Linq statement

In C#, I know how to use Regex.Replace a Linq query to replace substrings within an input string, as shown in this code sample.
var standards = _db.MapsFromOws.AsEnumerable().Select(m =>
m.Section).Distinct().AsEnumerable();
var enumerable = standards as IList<string> ?? standards.ToList();
const string elaPattern1 = #"LA.\d{1,2}-\d{1,2}.(\d{1,2}).([A-z]{1,2}).(\d{1,2})";
const string elaReplace1 = "$1.$2.$3";
var ela1 = enumerable
.AsEnumerable()
.Select(
m =>
new TranslationSelectModel
{
Section = m,
/* If m is a match to elaPattern1 then replace else m*/
Translation = Regex.Replace(m, elaPattern1, elaReplace1)
})
.OrderBy(m => m.Section).AsEnumerable();
This works well if there is only one pattern I need to replace, but what if I have to apply a set of pattern-replacements in the same list?
I had an idea of using a Dictionary<string,string> as a source of Regex patterns and replacement string. For example,
var regexPatternDictionary = new Dictionary<string, string>()
{
{#"LA.\d{1,2}-\d{1,2}.(\d{1,2}).([A-z]{1,2}).(\d{1,2})","$1.$2.$3"},
{#"MA.9-12.HS.([A-z])-([A-z]{0,3}).([A-z]).(\d)(.[A-z])*","HS.$1-$2.$3.$4$5"}
};
My question is how I would be able to use Regex.Replace() so that it matches each item in the enumerable to the regular expression dictionary instead of a single string variable?
The algorithm I'm seeing in my mind is:
For each item in enumerable
If item is a match to a dictionary, then apply replacement
Loop to next item
Not sure if I understand your problem 100%, but try something like this:
var result = enumerable.Select(x => replaceDictionary
.Aggregate(x, (y,z) => Regex.Replace(y, z.Key, z.Value))
.ToArray()

How to keep initializer list order within Select and/or SelectMany

I hope this is not a duplicate but I wasn't able to find an answer on this.
It either seems to be an undesired behavior or missing knowledge on my part.
I have a list of platform and configuration objects. Both contains a member string CodeName in it.
The list of CodeNames look like this:
dbContext.Platforms.Select(x => x.CodeName) => {"test", "PC", "Nintendo"}
dbContext.Configurations.Select(x => x.CodeName) => {"debug", "release"}
They are obtained from a MySQL database hence the dbContext object.
Here is a simple code that I was to translate in LINQ because 2 foreach are things of the past:
var choiceList = new List<List<string>>();
foreach (Platform platform in dbContext.Platforms.ToList())
{
foreach (Configuration configuration in dbContext.Configurations.ToList())
{
choiceList.Add(new List<string>() { platform.CodeName, configuration.CodeName });
}
}
This code gives my exactly what I want, keeping the platform name first which looks like :
var results = new List<List<string>>() {
{"test", "debug"},
{"test", "release"},
{"PC", "debug"}
{"PC", "release"}
{"Nintendo", "debug"}
{"Nintendo", "release"}};
But if I translate that to this, my list contains item in a different order:
var choiceList = dbContext.Platforms.SelectMany(p => dbContext.Configurations.Select(t => new List<string>() { p.CodeName, t.CodeName })).ToList();
I will end up with this, where the platform name isn't always first, which is not what is desired:
var results = new List<List<string>>() {
{"debug", "test"},
{"release", "test"},
{"debug", "PC"}
{"PC", "release"}
{"debug", "Nintendo"}
{"Nintendo", "release"}};
My question is, is it possible to obtain the desired result using LINQ?
Let me know if I'm not clear or my question lacks certain details.
Thanks
EDIT: So Ivan found the explanation and I modified my code in consequence.
In fact, only the Enumerable in front of the SelectMany needed the .ToList().
I should also have mentioned that I was stuck with the need of a List>.
Thanks everyone for the fast input, this was really appreciated.
When you use
var choiceList = dbContext.Platforms.SelectMany(p => dbContext.Configurations.Select(t => new List<string>() { p.CodeName, t.CodeName })).ToList();
it's really translated to some SQL query where the order of the returned records in not defined as soon as you don't use ORDER BY.
To get the same results as your nested loops, execute and materialize both queries, and then do SelectMany in memory:
var platforms = dbContext.Platforms.ToList();
var configurations = dbContext.Configurations.ToList();
var choiceList = platforms.SelectMany(p => configurations,
(p, c) => new List<string>() { p.CodeName, c.CodeName })
.ToList();
Rather than projecting it out to an array, project it out two a new object with two fields (potentially an anonymous object) and then, if you need it, project that into a two element array after you have retrieved the objects from the database, if you really do need these values in an array.
Try this-
var platforms= dbContext.Platforms.Select(x=>x.CodeName);
var configurations=dbContext.Configurations.Select(x=>x.CodeName);
var mix=platforms.SelectMany(num => configurations, (n, a) => new { n, a });
If you want to learn more in detail- Difference between Select and SelectMany

Take objects which attributes contains any element of array

I want to take those members whose name contains at least one of strings in array.
string[] words=content.TrimEnd().TrimStart().Split(' ');
So I want to choose that members which names contains any string from these words array, something like this
context.Members.Where(p.Name.Contains(word))
Also, I can't write Contains in LINQ expression, because it can't parse it to SQL. Which would i write in Where statement?
A potentially very expensive brute-force approach:
// untested
var names = context.Members.Select(m => m.Name).ToList();
names = names.Where(n => words.Any(w => n.Contains(w));
var a = new List<string>();
var b = new List<string>();
var c = a.Where(b.Contains);

Creating a Dynamic Where Clause as a Linq Expression

I am trying to dynamically append where conditions into a single expression-object, then pass that expression-object into a method that will use it. However, I keep getting "class name is not available at this point".
Thanks in advance!
UPDATE:
I finally was able to create a working example here.
The Code Look Like:
var view = new vw_QuickFindResult();
// This wont compile
Expression<Func<vw_QuickFindResult, bool>> where = Expression<Func<vw_QuickFindResult, bool>>(view, true);
// Build LIKE Statement
var searches = new List<String>(searchText.Split(' '));
searches.ForEach(productName =>
{
productName.Replace('"', '%');
productName.Replace('*', '%');
where = x => SqlMethods.Like(view.DocumentName, productName);
});
return DocumentCollectionService.ListQuickFind(where);
This is one problem:
where = x => SqlMethods.Like(view.DocumentName, productName);
You're ignoring the x here, instead using view which you've just initialized. I suspect you want:
where = x => SqlMethods.Like(x.DocumentName, productName);
However that will replace the existing where expression each time. I think you should be using PredicateBuilder by Joe Albhari. I'd avoid using ForEach too, personally:
var where = PredicateBuilder.False<vw_QuickFindResult>();
// Build LIKE Statement
foreach (string productName in searchText.Split(' '))
{
string like = productName.Replace('"', '%');
.Replace('*', '%');
where = where.Or(x => SqlMethods.Like(view.DocumentName, like));
}
return DocumentCollectionService.ListQuickFind(where);
(I'm guessing at the functionality you want; you may want PredicateBuilder.True and the And extension method instead.)

How can I build Linq query with dynamic OR statements?

The following code:
var dynamicQuery = from a in _context.Users select a;
string[] args = new string[] { "aa", "bb", "cc" };
foreach (string word in args)
dynamicQuery = dynamicQuery.Where(x => x.Name.Contains(word));
return dynamicQuery.ToList();
Will allow me to create a Linq query with a dynamic list of AND expressions.
But suppose I wanted to do the same, only with a dynamic list of OR expressions?
You don't need to loop at all:
return _context.Users.Where(x => args.Any(word => x.Name.Contains(word)));
EDIT: More generally, you can use:
Func<User, bool> predicate = user => false;
foreach (var item in items)
{
var predicateCopy = predicate;
predicate = user => predicateCopy(user) || someOtherCondition;
}
return query.Where(predicate);
This will end up with quite deep stacks (with one delegate calling another calling another etc). Where the specific situation allows you to use Any, that would usually be a better approach.
I would expect Any to work in most situations where you've got a collection of items to potentially match against... the non-Any approach is approprate for "in some situations, anyone over 18 is fine... in some situations anyone with a last name beginning with "G" is appropriate, etc.

Categories