How specific are LINQ .Contains IEnumerable results? - c#

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.

Related

WIQL "not contains words" returns false positives

I am running a WIQL query that contains this:
"And [Microsoft.VSTS.Common.AcceptanceCriteria] Not Contains Words '*Given*When*Then*'";
I would expect this to not return any AcceptanceCriteria that contain the string "Given" followed by "When" followed by "Then". However I am receiving this item in the results:
"Microsoft.VSTS.Common.AcceptanceCriteria":"<div></div><div><b>Given</b></div><div>Frontier has to submit the replenishment order creation request to Maestro</div><div><b>When</b></div><div>Prophet sends the replenishment order recommendation via REPLENORD feed</div><div><b>Then</b></div><div>Frontier should be able to determine and remove the logic to include the PLANNER_ID / SCS_ID as mandatory information needed for replenishment order creation</div>"
Am I using this wrong?
When using the query operators Contains Words or Does Not Contain Words to search with the wildcard character (*), you can only use the wildcard character at the end of a partial word or phrase.
In your case, the expression you set (*Given*When*Then*) obviously does not obey this restriction. So, it is a wrong expression.
For more details, you can see this document.

Should I use string.Contains() before string.Replace()?

Is it unnecessary to have this if statement before doing a string replace?
if (myString.Contains(oldValue))
{
myString = myString.Replace(oldValue, newValue);
}
All the details are in the documentation for String.Replace:
Return Value:
A string that is equivalent to the current string except that all instances of oldValue are replaced with newValue. If oldValue is not found in the current instance, the method returns the current instance unchanged.
The if statement is not required.
An if statement is not even a performance optimization, since String.Replace returns the same object instance, if oldValue is not found. I have verified this using the following code:
namespace StringReplaceTest
{
class Program
{
static void Main(string[] args)
{
string s = "Test";
string s2 = s.Replace("Foo", "Bar");
string s3 = s.Replace("es", "tt");
}
}
}
Using the handy Make Object ID feature (right-click on a symbol in the Locals, Auto, or Watch window; see Common Expression Evaluator Features for more details) produced the following output:
s | "Test" {$1}
s2 | "Test" {$1}
s3 | "Tttt" {$2}
Since the String.Replace() function doesn't throw an exception if the string doesn't contain the value specified, it's unnecessary to verify it. It will go through an unnecessary condition.
I know this is an old thread, but just wanted to provide my 2ยข!
Please, let me know if what I'm saying is wrong, but I just tried it on my environment and seemed plausible! :)
For the most part, yes, the replace function will do the contains for you therefore it's unnecessary to have it separately. However, it is advantageous to do so if your "new value" is derived from a method - depending on how much work is done to get that value within the method!
Example when it not advantageous:
I'm working on making "String Replacers" for the body of an email sent by an application. For example, if the user has "[[User Name]]" within the body of the email, I want to replace that with the recipients first name. Thankfully, our framework has that information stored in memory, so I don't mind doing a single replace for that.
Example when it is advantageous:
Under the same premise from above, another replacer is "[[Payments]]". This would go to the database, and bring up a log of the top 1000 transactions the user made, then do some work on the data retrieved and show details on the body of the email. Imagine having to do all of that every time the user sends an email from the app, even if it doesn't contain the "[[Payments]]" keyword! Having the contains before calling my method that comes up with the string value of "[[Payments]]" really pays off here!
Long story short, it depends on your implementation, but for the most part - yes, it's unnecessary.
In me experience, if you will replace many time (and you need one replace) better checking with Contains(). In my case is help my code be more fast (almost doubled).

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.

Linq with dynamics "where parameter"

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.

Perform Operation on Named Capture Group in regex.Replace

I apologize in advance if this is a duplicate--- I can't imagine that it hasn't been asked before- but I couldn't seem to find it.
Why does the 2nd c# statement below throw an exception?
var regex = new Regex(#"\[IMG(?<image_number>[0-9]+)\]");
regex.Replace("[IMG1]", int.Parse("${image_number}").ToString());
I know I can access the named group, but I can't perform an operation on it-- in this case int.Parse(). When I try to use the named group in this way, it just gives me the string "${image_number}"-- which of course can't be parsed as an integer.
Thanks.
The Regex class is not magic.
It cannot magically insert the captured value in string literals that appear inside Replace calls.
Instead, if you pass a string referencing a capture group to the Replace() method, it will parse out the string and insert the value.
You need to pass a lambda expression:
regex.Replace("[IMG1]", m => int.Parse(m.Groups["image_number"].Value).ToString());

Categories

Resources