C# Dynamic where for a collection - c#

I want to search a collection based on text box. The user should be allowed to type in multiple words and in any order. Meaning if the string in the collection is "What a happy day" and the user types in "day What" the string should appear. Now I know how to do with with hard coding the number of words allowed (for example only 3 words allowed) with something like this;
nc = oc.Where(X => X.SearchData.IndexOf(words[0]) > -1 || X.SearchData.IndexOf(words[1]) > -1 || X.SearchData.IndexOf(words[2]) > -1);
note: yes I know I would have to protect to make sure there was actual 3 values in the array words but that is not shown.
The problem with this is that it limits the user and I don't want to do that. If the user wants to search off 10 or 20 things then that is fine with me.
Is there a way to dynamically create the Where statement for collection oc?
thanks

You need more LINQ:
oc.Where(x => words.Any(w => x.SearchData.IndexOf(w) > -1))

IndexOf(w) returns true even if w is a matched substring. For instance in your example if user enters Wha then it gets matched with What. As I understand you it is not the case. So you can simply split SearchData and search over it:
var enteredWords = SearchData.Split();
return oc.Where(p=> enteredWords.Any(q=>p.Contains(q));

I think the answer of #Slaks will match on partial words, as per my comment and the answer given by #Alireza
You could try
oc.Where(phrase => phrase.Split().Intersect(SearchData.Split()).Count() > 0);
There are always various ways with LINQ...

Related

How to compare inexact string values

I want to compare two string values which are not exact For example I want to compare Admin to Administrator, this should return true or should execute.
I tried contain which is not working
var prodcut = lstProducts.Where(i => i.Name.ToLower().Contains(appname.ToLower())).FirstOrDefault();
Above code not working if i.Name is 'Admin' and appname.ToLower() is 'Administrator'. It just return null but want it should detect values.
If you want to check it both ways so if A contains B OR if B contains A you can use the || operator (the OR operator) like so:
a.Contains(b) || b.Contains(a)
You've got the strings the wrong way around (you're looking for Adminstrator in Admin)
You can do the check both ways around like this:
lstProducts.Where(i =>
i.Name.ToLower().Contains(appname.ToLower()) ||
appname.ToLower().Contains(i.Name.ToLower())
).FirstOrDefault();
Or just compare the first few characters:
lstProducts.Where(i =>
i.Name.ToLower().SubString(0,5) == appname.ToLower().SubString(0,5))
).FirstOrDefault();
Fuzzy matching is actually quite a complicated subject but there's a lot of research into the topic.

How to deal with text input containing a last name with a space or a first name [space] last name combination

I'm dealing with a problem that I can't wrap my head around and could use your help and expertise.
I have a textbox that allows the user to search for another user by a combination of name criterias listed below:
<first name><space><last name> (John Smith)
<last name><comma><space|nospace><first name> (Smith, John) or (Smith,John)
Either starting portion of first name or last name (in this case, I do a search against both the first and last name columns) (Smith), (John), (Sm), or (Jo)
Issue:
There are quite a few users who have a space in their last name, if someone searches for them, they may only enter "de la".
Now in this scenario, since there is a space between the words, the system will assume that the search criteria is first name starts with "de" and last name with "la".
The system will work as expected if the user typed "de la," because now the input contains a comma, and the system will know for sure that this search is for a last name but I have to assume that not everyone will enter a comma at the end.
However the user probably intended only to search for someone with last name starting with "de la".
Current options
I have a few options in mind and could use your help in deciding which one would you recommend. And PLEASE, feel free to add your suggestions.
User training. We can always create help guides/training material to advise the users to enter a comma at the end if they're searching for a last name containing a space. I don't like this approach because the user experience isn't smart/intuitive anymore and most of the users won't read the help guides.
Create 2 different text boxes (for first name and last name). I'm not a fan of this approach either; the UI just won't look and feel the same and will prove inconvenient to the users who just want to copy/paste a name from either Outlook or elsewhere (without having to copy/paste first/last name separately).
Run the search criteria with first, and then in addition, run a search for people with spaced last name and append both results to the return value. This might work, but it'll create a lot of false positives and cause extra load on the server. E.g. search for "de la" will return Lance, Devon (...) and "De La Cruz, John" (...).
I'd appreciate any type of feedback you can shed on this issue; your experiences, best practices, or the best one, some code snippets of something you've worked with related to this scenario.
Application background: Its ASP.NET (4.0) WebAPI service written in C#; its consumed by a client sitting on a different server.
I've used this technique for a number of years and I like it.
Lose the comma, no one will use it. If there is not a space, search for first OR last. If there is a space, search for first AND last. This code works very well for partial name searches, i.e. "J S" finds Jane Smith and John Smith. "John" will find "John Smith" and "Anne Johnson". This should give you a pretty good starting point to get as fancy as you want with your supported queries.
public IEnumerable<People> Search(string query, int maxResults = 20)
{
if (string.IsNullOrWhiteSpace(query))
{
return new List<People>();
}
IEnumerable<People> results;
var split = query.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
if (split.Length > 1)
{
var firstName = split[0];
var lastName = string.Join(" ", split.Skip(1));
results = PeopleRepository.Where(x =>
x.FirstName.StartsWith(firstName, StringComparison.OrdinalIgnoreCase) &&
x.LastName.StartsWith(lastName, StringComparison.OrdinalIgnoreCase));
}
else
{
var search = split[0];
results = PeopleRepository.Where(x =>
x.FirstName.StartsWith(search, StringComparison.OrdinalIgnoreCase) ||
x.LastName.StartsWith(search, StringComparison.OrdinalIgnoreCase));
}
return results.Take(maxResults);
}
Maybe the point is that you should index your user data in order to look for it efficiently.
For example, you should index first and last names without caring about if they're first or last names. You want to search people, why end-user should care about search term order?
The whole index can store user ids on sets specialized by names (either first or last names). If user ids are integers, it would be something like this:
John => 12, 19, 1929, 349, 1, 29
Smith => 12, 349, 11, 4
Matias => 931, 45
Fidemraizer => 931
This way user inputs whatever and you don't care anymore about ordering: if user types "John", you will show all users where their ids are in the John set. If they type both John Smith, you'll need to intersect both John and Smith sets to find out which user ids are in both sets, and so on.
I don't know what database technology you're currently using, but both SQL and NoSQL products can be a good store for this, but NoSQL will work better.

How is it possible to implement regular expressions (or the like) into WebMatrix razor C#?

I am trying to use regular expressions with my C# code, and I can find a few examples here on what those regular expressions might look like for my cause, but nowhere can I tell how I am supposed to (syntactically) implement that into my logic and code.
I have tried setting up an external .cs file and then calling the method, but the value returned is unable to be casted or worked with in any way.
The code can be found here:Checking strings for a strong enough password
In any event, I just want to know how I can check (and test against) values in a password to make sure it is up to standards that I specify.
A better way than suggesting to use regular expressions (since information on how to incorporate them into my own specific logistical setup is both very sparse and very confusing)
...is suggesting how it can be done without them.
I know I could use
foreach(char c in myString)
and then test individually, character by character, but I was hoping there was a better way that can either be regex, explained (that is, explained how to call this suggestion into action, not just posting a string of seemingly (but not) random characters and told, hey use that!), or not using regex at all, but somekind of shorthand, perhaps.
Truth is I would use regular expressions, but every time I look them up I can't seem to find anything that is useable by me in WebMatrix.
I want to be able to test a password to be sure that it has both an uppercase and a lowercase number. In addition I need to check for at least one number.
UPDATE:
Okay, allow me to rephrase my question, maybe I am being confusing...
How does regex work in webmatrix razor (C#). Please show how the regex actually works (I can't seem to find a clear, or even close to clear, answer on this on the web for webmatrix), then please show how it can be put (directly or indirectly) into if logic, on my cshtml page, so that I can actually check against it.
A Regular Expression (Regex), as you will find out, is a tool used in matching text against a pattern and even extracting or replacing matches in the source text.
To do this the Regex engine (which exists in the .Net framework inside the namespace System.Text.RegularExpressions) uses some established patterns that represent certain kinds of chars.
To use a Regex, you pass it the pattern agains which a text will be tested and the text itself.
For instance, the following snippet tests if there are lowercase letters in the text:
using System.Text.RegularExpressions;
...
var Pattern = "[a-z]";
if (Regex.IsMatch(SomeText, Pattern)) {
//... there is at least one lower case char in the text
}
The pattern used above estates that we are interested in a range of chars from lowercase "a" to lowercase "z".
A pattern to require at least one lowercase letter, one digit and one uppercase letter could probably be something like
#"[\d]+[a-z]+[A-Z]|[\d]+[A-Z]+[a-z]|[a-z]+[\d]+[A-Z]|[a-z]+[A-Z]+[\d]|[A-Z]+[\d]+[a-z]|[A-Z]+[a-z]+[\d]"
As you can see, a Regex pattern can be as simple as in the first example, but may escalate fast in complexity depending on the kind of information you want to check or extract.
In the Regex above, the items between square brackets are called character classes. The plus sign after an element is a quantifier, it indicates that the item may appear one or more times. Items separated by a vertical bar indicate alternatives. The "\d" pattern represents any digit from "0" to "9".
You don't really need a Regex to check the password strength in your application, but using then instead of the explicit tests you are currently making would (in my opinion), greatly improve your program's readability:
using System.Text.RegularExpressions;
...
if (!Regex.IsMatch(password, "[A-Z]")) {
errorMessage = "Your password must contain at least one uppercase character.";
} else if (!Regex.IsMatch(password, "[a-z]") {
errorMessage = "Your password must contain at least one lowercase character.";
} else if (! Regex.IsMatch(password, #"[\d]")){
errorMessage = "Your password must contain at least one numerical character.";
}
...
If you're trying to do this test on the page before the information is sent to the server, you can check with javascript and regex. I think this way is best, since you eliminate additional trips to the server. It can pass the javascript validation, post the data to your site, and you can validate more on the server (in case the user has javascript off). Razor syntax helps in the page rendering, but won't help you when the user is doing stuff on the page.
Pattern: ((?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,15})
This is requiring at least 1 digit, 1 lowercase and 1 uppercase. It's also requiring the password to be between 8 and 15 characters long (inclusive). You can easily change the length requirements in the pattern. For example, if you wanted it to be at least 8 characters, but no max:
((?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,})
To use this in javascript (I did use jQuery to wire up my click event in my test and get the password from the input):
var password = $('#password').val();
var pattern = /((?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,15})/;
var pass = pattern.test(password);
A Fiddle showing it in action: http://jsfiddle.net/gromer/chF4D/1/
Well I know this isn't the way to properly do this, (the proper way is using regular expressions, which is what I was attempting to figure out) but if you are like me, and sick of asking/searching the same question over and over again, without getting any entirely useful feedback, this is what worked for me.
int Ucount = 0;
int Lcount = 0;
int Ncount = 0;
foreach(char c in password)
{
if(c!='A' && c!='B' && c!='C' && c!='D' && c!='E' && c!='F' && c!='G' && c!='H' && c!='I' && c!='J' && c!='K' && c!='L' && c!='M' && c!='N' && c!='O' && c!='P' && c!='Q' && c!='R' && c!='S' && c!='T' && c!='U' && c!='V' && c!='W' && c!='X' && c!='Y' && c!='Z')
{
Ucount++;
if(Ucount >= password.Length)
{
errorMessage = "Your password must contain at least one uppercase character.";
}
}
if(c!='a' && c!='b' && c!='c' && c!='d' && c!='e' && c!='f' && c!='g' && c!='h' && c!='i' && c!='j' && c!='k' && c!='l' && c!='m' && c!='n' && c!='o' && c!='p' && c!='q' && c!='r' && c!='s' && c!='t' && c!='u' && c!='v' && c!='w' && c!='x' && c!='y' && c!='z')
{
Lcount++;
if(Lcount >= password.Length)
{
errorMessage = "Your password must contain at least one lowercase character.";
}
}
if(c!='0' && c!='1' && c!='2' && c!='3' && c!='4' && c!='5' && c!='6' && c!='7' && c!='8' && c!='9')
{
Ncount++;
if(Ncount >= password.Length)
{
errorMessage = "Your password must contain at least one numerical character.";
}
}
}
Again, please note that the proper way to do this is to use regular expressions, but this code works fine for me, and I got to end the wild goose chase that was understanding regular expressions.
UPDATE:
Or, if you want to do it right, read Branco Medeiros's post above, he posted after I posted this, and his way is the right way to do it (provided you don't want to do it with JavaScript before it is sent to the server. If your app is like mine and not resource intensive enough to need JavaScript to do this, use Branco Medeiros's example above).

RegEx expressions with replacement and extracting values

I want to take an expression like
(123456789..value > 2000) && (987654321.Value < 12)
extract the 123456789 and 987654321 (could be anything here)
and replace it with
ClassName.GetInfo("%s").GetValue() (as an example)
putting the 2 values in the place of the %s...
to be a resulting
(ClassName.GetInfo("123456789").GetValue() > 2000) && (ClassName.GetInfo("987654321").GetValue() < 12)
Can anyone give me a clue as to how to accomplish this?
A rather oversimplified example, but this should work.
Note that the following will only allow alpha-numeric or '-' or '_' in the place you claim (could be anything here). This is by nessesity if you intend to be able to recognize it with any form of parser regex or otherwise. You need to either limit the characters that can be used as an identifier, or you need to delineate them and allow for escaping the delimitation characters.
private static void Main()
{
Regex pattern = new Regex(#"(?<Name>[\w\-_]+)\.+(?<Value>[\w\-_]+)");
string sample = #"(123456789..value > 2000) && (987654321.Value < 12)";
string result = pattern.Replace(sample,
m =>
String.Format(
"ClassName.GetInfo(\"{0}\").Get{1}{2}()",
m.Groups["Name"].Value,
Char.ToUpper(m.Groups["Value"].Value[0]),
m.Groups["Value"].Value.Substring(1))
);
Console.WriteLine(result);
}
The program outputs:
(ClassName.GetInfo("123456789").GetValue() > 2000) && (ClassName.GetInfo("987654321").GetValue() < 12)
There are two other rather odd behaviors in your example that are addressed above. The first is the use of multiple delimiters '..' in your example "(123456789..value". This seems like a possible mistake, just remove the '+' from this part of the expression ").+(".
The second oddity is that your example just auto-magically corrects the character-case of the first property from "value" to "Value". Although I mimic this magical behavior by ensuring the first character is upper-case this is not a great solution. A better answer would be to use a case-insensitive dictionary and lookup the proper case.
Hopefully that will get you started, but I have to be honest and say you have a VERY long road ahead of you. Parsing an expression language is never a trivial thing and should generally be avoided. If this is for internal use just make them type in the full version. If this is for external use... well, I would re-think you're objective. Perhaps building a graphical expression tree like SQL's QBE would be a better expenditure of your time and energy.

String-parsing-fu: Can you help me find a way to retrieve this value?

I need to somehow detect if there is a parent OU value, and if there is retrieve it.
For example, here there is no parent:
LDAP://servera/OU=Santa Cruz,DC=contoso,DC=com
But here, there is a parent:
LDAP://servera/OU=Ventas,OU=Santa Cruz,DC=contoso,DC=com
So I would need to retrieve that "Ventas" string.
Another example:
LDAP://servera/OU=Contabilidad,OU=Ventas,OU=Santa Cruz,DC=contoso,DC=com
I would need to retrieve that "Ventas" string as well.
Any suggestions on how to tackle this?
string ldap = "LDAP://servera/OU=Ventas,OU=Santa Cruz,DC=contoso,DC=com";
Match match = Regex.Match(ldap, #"LDAP://\w+/OU=(?<toplevelou>\w+?),OU=");
if(match.Success)
{
Console.WriteLine(match.Result("${toplevelou}"));
}
I'd find the first occurrence of OU=... and get it's value. Then I'd check if there was another occurrence after it. If so, return the value I've got. If not, return whatever it is you want if there's no parent (String.Empty, or, null, or whatever).
You could also use a regular express like this:
var regex = new Regex(#"OU=(.*?),");
var matches = regex.Matches(ldapString);
Then check how many matches there are. If >1 return the captured value from the first match.
Update
The regex above needs to be improved to allow the case where there's an escaped comma (\,) in the LDAP string. Maybe something like:
var regex = new Regex(#"OU=((.*?(\\\,)+?)+?),");
That may be broken, and there may be simpler way to do the same thing. I'm not a regex wizard.
Another Update
Per Kimberly's comment below the regex should be #"OU=((?:.*?(?:\\\,)*?)+?),".
Call me crazy, but I 'd do it this way (hey ma, look, an one-liner!):
var str = "LDAP://servera/OU=Ventas,OU=Santa Cruz,DC=contoso,DC=com";
var result = str.Substring(str.LastIndexOf('/') + 1).Split(',')
.Select(s => s.Split('='))
.Where(a => a[0] == "OU")
.Select(a => a[1])
.Reverse().Skip(1).FirstOrDefault();
result is either null or has the string you want. This will work no matter how many OUs are in there and return the second-to-last one, as long as the format of the string is valid to begin with.
Update: possible improvements:
The above will not work correctly if your DN contains an escaped forward slash or an escaped comma.
To fix both of these you need to use regular expressions. Change:
str.Substring(str.LastIndexOf('/') + 1).Split(',')
to:
Regex.Split(Regex.Split(str, "(?<!\\\\)/").Last(), "(?<!\\\\),")
What this does is separate the DN by getting the last part of str after splitting on forward slashes, and split the in parts DN by splitting on commas. In both cases, negative lookbehind is used to make sure that the slashes/commas are not escaped.
Not as pretty, I know. But it's still an one-liner (yay!) and it still allows you to use LINQ further down to handle multiple OUs any way you choose to.

Categories