Linq with dynamics "where parameter" - c#

I have this case:
I create an array from a list like this:
String[] parameters = stringParametersToSearch.Split(' ');
The number of parameters can vary from 1 to n and I have to search for objects that in the description field containing all the occurrences of parameters
List<LookUpObject> result =
components.Where(o => o.LongDescription.Contains(parameters[0])).ToList<LookUpObject>();
if the parameter is 1 do so, but if they had two or more?
Currently to resolve this situation, I use an IF in which I build the LINQ expression for cases up to five parameters (maximum of real cases).
I can resolve this situation dynamically using LINQ ?

You either want to use Any or All, depending on whether you want to find objects where all of the parameters match or any of them. So something like:
var result = components
.Where(o => parameters.Any(p => o.LongDescription.Contains(p)))
.ToList();
... but change Any to All if you need to.
It's always worth trying to describe a query in words, and then look at the words you've used. If you use the word "any" or "all" that's a good hint that you might want to use it in the query.
Having said that, given the example you posted (in a now-deleted comment), it's not clear that you really want to use string operations for this. If the long description is:
KW=50 CO2=69 KG=100
... then you'd end up matching on "G=100" or "KG=1" neither of which is what you really want, I suspect. You should probably parse the long description and parameters into name/value pairs, and look for those in the query.

Related

How specific are LINQ .Contains IEnumerable results?

Assuming I have a Users table having several users with Username column values that follow:
Ortund
Richard
Happy McHappyFace
Flapjack
Harvey
Tabitha
Asha
If I query users with .Contains() on my LINQ query based on user input "Happy":
var users = db.Users.Where(x => x.Username.Contains("Happy")).ToList();
Is the IEnumerable going to return every user record having "Ha" in the name (Richard, Happy McHappyFace, Harvey, Tabitha Asha), for example or will it return just the "Happy McHappyFace" user?
Why would it return any match for Ha? You're specifically asking for users whose name contains Happy (in that capitalization, even), so you're only going to get one result here.
The documentation from String.Contains says:
Returns a value indicating whether a specified substring occurs within this string.
So the entire match string (Happy) must match the whole or a part from the input string (Happy McHappyFace). It will only match on Happy, not on H or Ha.
It will translate to where username like '%Happy%', which will match Happy McHappyFace only.
There is a slight catch though (but not in this case): LINQ has a Contains method (specifically Enumerable.Contains) that could match the signature (which would match on a character enumerable, which string happens to be). Since it is an extension method, the method belonging to the class will match first.

C# LINQ Contains method with two clauses

What is the best practice way to use two clauses in LINQ Contains method..
Title is string
This is my If statement :
if (oWeb.ParentWeb.Title.Contains("Ricky") || oWeb.ParentWeb.Title.Contains("John"))
I need solution like this :
if (oWeb.ParentWeb.Title.Contains("Ricky", "John"))
Since Title is string this actually has nothing to do with LINQ as the used Contains method is an instance method of string.
Assuming you have more strings to check, you can do something like that:
var strings = new[] {"Ricky", "John"};
if (strings.Any(oWeb.ParentWeb.Title.Contains))
// do something
But roryap's answer using a regex seems preferable (as long as the number of strings to check is not too big).
I don't think LINQ is the best option for that. Why not use regular expressions?
Regex.IsMatch(oWeb.ParentWeb.Title, "ricky|john", RegexOptions.IgnoreCase);
Contains takes only one parameter, so you cannot do it this way.
You can make an array of items, and check containment with it:
var titles = new[] {"Ricky", "John"};
if (titles.Any(t => oWeb.ParentWeb.Title.Contains(t))) {
...
}

linq-to-query and contains for an IEnumerable<string>?

I have list of strings IEnumerable<string> companies
containing companies i.e. mcdonalds, sony inc.
I want to compare with values in the database. I grab the list from database and in the foreach loop I compare
if (companies.Any(c => c.Contains(name.ToLower())))
{...}
in the database I have companies i.e. mcdonalds inc, sony
when searching using "sony" it finds it. When I search using "mcdonalds inc" it doesn't. because of the additional word "inc"
I know I am compare companies.any(contains(mcdonalds inc)) and it doesnt find it.
Any suggesting on how I can extend the if condition to also compare via verse
Why not compare your list of strings with the database, rather than the database with your list?
foreach(var name in list)
{
if(table.Any(t => SqlMethods.Like(name, string.Format("%{0}%", t.Column))
{ ...
}
}
That will capture differences where the names are broadly, but not exactly the same.
You could call Split before comparing and compare only the first element in the list. In this case you would always discard the 'inc'.
This will of course only work if your problem only happens because you may have suffixes that have to be discarded.
It depends on how fuzzy you need your string matching to be. You probably can't just remove all occurrences of inc from the string, for example, because a company named "The inc company" or "Inception" will potentially get affected.
For your specific example, you basically need to strip some terms (let's say inc, incorporated, and llc) from the end of the string. Let's also say that there might be more than a single term at the end of the string. You can potentially do this with a regex, something like
Regex termRemover = new Regex("^(?<companyName>.*?)(\\s+(inc|incorporated|llc))*$");
which could then be used in your example like
string scrubbedName = termRemover.Match(name.ToLower()).Groups["companyName"].Value;
if (companies.Any(c => c.Contains(scrubbedName)))
{...}
(plus error checking, etc, dropped here for brevity). The companies list should be scrubbed in the same way prior to use; otherwise your sony inc will never match anything that scrubs to just sony.

Efficient means for parsing and replacing values in T-SQL

I have a lot of T-SQL queries I need to parse and replace certain values.
A query may look like this
SELECT dbo.udfTest(123,'Bob') as Foo
or alternatively
SELECT dbo.udfTest(123) as Foo
My task is to replace the number value with another given value but as I contemplate just rolling something up using the string class and doing substrings etc I start to run into lots of edge cases like this
SELECT dbo.udfTest ( 123 ) as Foo
or
SELECT [dbo].[udfTest]( 123 ) as Foo
or
SELECT [dbo].[udfTest]( 123 ) as Foo1, dbo.udfTest(123) as Foo2
Throw in any combination of whitespace, casing, brackets, nested parenthesis and you can imagine the number of variations I would have to cover...nasty.
Which brings me to wondering if there is a better way? RegEx may be a play but I figured I would toss it out to get some opinions.
You might be able to use the database features of Visual Studio. See API Reference for Database Features of Visual Studio, especially the Microsoft.Data.Schema.ScriptDom Namespace.
Also, a quick search for "parse mdx query" turned up several interesting hits. I'm pretty sure I once found a tool that could parse MDX queries, then use the parse tree to create a formatted version.
Perhaps the article Getting to the Crown Jewels will help. If nothing else, it may give you a hint about who to ask for help.
Matching multiple possible patterns says RegEx right off the bat. The RegEx will be ugly but it should get you where you want to go pretty easily.
If the end goal is to replace some set of numbers with a new set of numbers (basically a translation), wouldn't it be easier update your UDF to use a lookup table to map the old values to the new ones instead of trying to intercept every method call before it takes place? (Unless your examples have been greatly simplified in that they only show the same function being called each time, but in reality there's many that would have to be updated)

C# Multi Replace Idea

I know the want for a "multi replace" in C# is no new concept, there are tons of solutions out there. However, I haven't came across any elegant solution that jives well with Linq to Sql. My proposal is to create an extension method (called, you guessed it, MultiReplace) that returns a lambda expression which chains multiple calls of Replace together. So in effect you would have the following:
x => x.SomeMember.MultiReplace("ABC", "-")
// Which would return a compiled expression equivalent to:
x => x.SomeMember.Replace("A", "-").Replace("B", "-").Replace("C", "-")
I'd like your thoughts/input/suggestions on this. Even if it turns out to be a bad idea, it still seems like a wicked opportunity to dive into expression trees. Your feedback is most appreciated.
-Eric
I'm not completely sure why you would want to do what you're describing. However if your motivation is to make more readable linq statements, by condensing some filtering logic, I suggest to look into the Specification Pattern.
If you only want to transform the result however, I would suggest to just do it in code, as there would only be a marginal benefit transforming on the server.
Some more examples on the Specification Pattern and Linq-to-SQL
I'm not sure what MultiReplace should be doing, or why you want to mix it with Linq to Sql. (Anything truly working with Linq to Sql would be translatable into SQL, which would be quite a lot of work, I think.)
The best solution I can think of is Regular Expressions. Why not use them? Linq to Sql may even translate them for you already, since MS SQL supports regular expressions.
x => Regex.Replace(x, "A|B|C", "-")
To me, this seems like a REALLY bad idea, it could be just because of your example, but the syntax you have listed to me is very confusing.
Reading this I would expect that
x => x.SomeMember.MultiReplace("ABC", "-")
If using the following text
ABC This Is A Test ABC Application
You would get something like
--- This Is A Test --- Application
But you are actually saying that it would be
--- This Is - Test --- -pplication
Which I see as problematic....
I'd also mention here that I don't really see a n urgent need for something like this. I guess if there was a need to do multiple replacemnts I'd do one of the following.
Chain them myself "myInput".Replace("m", "-").Replace("t", "-")
Create a function/method that accepts an ARRAY or List of strings for the matches, then a replacement character. Keeping it easy to understand.
Maybe there's no method that will do this, but you could simply nest the calls. Or make a static method that takes in a string and an array containing all its replacements. Another problem is that you need to actually specify a pair (the original substring, and its replacement).
I've never needed/wanted a feature like this.
The best way to do this would be:
static string MultiReplace(string this CSV, string Orig, string Replacement)
{
string final = "";
foreach (string s in CSV.Split(','))
{
final += s.Replace(Orig, Replacement);
}
return final;
}
Then you could call it with no ambiguity:
x => x.SomeMember.MultiReplace("A,B,C", "-")
If you expect that these strings will be longer than 3-4 values in the CSV, you might want to drop the concatenation in favor of a StringBuilder.

Categories

Resources