Is there a way to combine foreach loop - c#

I have 2 lists as follows:
valuesToMatch = ["someemail#something.com", "123-45-6789"]
regexValuesToMatchWith = ["^[a-zA-Z0-9._%+-]+#[a-z0-9-]+\.(com|org|edu|)(\.[a-z]{2,3})?", "^\d{3}-\d{2}-\d{4}$"]
I want to match the email with email regex and SSN number with SSN regex. If both are true, then only I can add data, not otherwise.
public bool MatchRegex(Data data)
{
var regexMatch = false;
var valuesToMatch = GetValuesToMatch(data.values);
var regexValuesToMatchWith = service.Validations(data.id);
foreach (var toMatch in valuesToMatch)
{
foreach (var withRegex in regexValuesToMatchWith)
{
var r = new Regex(withRegex, RegexOptions.IgnoreCase);
if (r.IsMatch(toMatch))
{
regexMatch = true;
}
}
}
if (regexMatch)
{
return dbContext.Add(data);
}
return false;
}
I have looked at approaches online but I am not sure if this is possible to achieve.

Maybe a for loop would be more suitable for what you're trying to achieve.
bool regexMatch = true;
for (int i = 0; i < withRegex.Length; i++)
{
var r = new Regex(valuesToMatch[i], RegexOptions.IgnoreCase);
if (!r.IsMatch(regexValuesToMatchWith[i]))
{
regexMatch = false;
break;
}
}
}
The foreach loop will compare each value with both email and SSN regex, which is unnecessary. Besides that, if you set regexMatch to true like you're doing, it's going to be true even if one of the comparisons doesn't match.
I'm assuming valuesToMatch and regexValuesToMatchWith are always of the same size and with data in correct order, based on your example.

Related

Regex alternative for extracting numeric and non-numeric strings

Using the below expression, I'm able to get the expected output and extract numbers or string and split to a string array.
Regex _re = new Regex(#"(?<=\D)(?=\d)|(?<=\d)(?=\D)", RegexOptions.Compiled);
_re.Split("2323dfdf233fgfgfg ddfdf334").Dump(); //string can be any alphanumeric start with
How to achieve the same thing without using Regex? Do I need to parse each char and segregate? I have a large array of text which needs to be processed to extract but I cannot use regex as inputs provided.
For a Linq solution, you can combine the use of Enumerable.Skip() and Enumerable.TakeWhile() while checking for char.IsDigit() to determine whether the character is a digit or not. For example:
string inputString = "2323dfdf233fgfgfg ddfdf334";
var list = new List<string>();
int usedLength = 0;
while (usedLength < inputString.Length)
{
bool isDigit = char.IsDigit(inputString[usedLength]);
string item = string.Concat(inputString.Skip(usedLength).
TakeWhile((c) => char.IsDigit(c) == isDigit));
usedLength += item.Length;
list.Add(item);
};
Then you can easily iterate through the list:
foreach (string item in list)
Console.WriteLine(item);
Output:
2323
dfdf
233
fgfgfg ddfdf
334
This solution is fast enough. Check with larger strings.
string str = "2323dfdf233fgfgfg ddfdf334";
var strings = new List<string>();
var sb = new StringBuilder();
var lastCharIsNumber = char.IsDigit(str[0]);
foreach (var c in str) {
if (char.IsDigit(c) ) {
if (!lastCharIsNumber) {
strings.Add(sb.ToString());
sb.Clear();
}
lastCharIsNumber = true;
}
else {
if (lastCharIsNumber) {
strings.Add(sb.ToString());
sb.Clear();
}
lastCharIsNumber = false;
}
sb.Append(c);
}
strings.Add(sb.ToString());
strings.Dump();

Comparing multiple strings to list of regular expressions C#

I am trying to compare multiple strings to a list of regular expressions. I am trying to make it so i can compare all the strings i have to all of the regular expressions and if it DOESNT match any of the regular expressions then it gets put into a list so i know it doesnt match. My method works when i have only 1 regular expression but does completely quits working when i add 2 or more.
public List<string> CheckNames(List<string> nameList, List<string> regexList)
{
var missMatchNameList = new List<string>();
foreach (var name in nameList)
{
foreach(var regex in regexList)
{
if (!Regex.IsMatch(name, regex))
{
missMatchNameList.Add(name);
break;
}
}
}
return missMatchNameList;
}
that is my code so far, what am i missing here?? Any tips would be great!
Change this
foreach (var name in nameList)
{
foreach(var regex in regexList)
{
if (!Regex.IsMatch(name, regex))
{
missMatchNameList.Add(name);
break;
}
}
}
To
foreach (var name in nameList)
{
bool match = false;
foreach(var regex in regexList)
{
if (Regex.IsMatch(name, regex))
{
match = true;
break;
}
}
if (!match)
missMatchNameList.Add(name);
}
Your problem was that as soon one of you regex expressions didn't match, it got added to the list, even if it matched another regex.
Also, by keeping the break; you don't carry on processing all the other regexs after you find a match.
Try this one
public List<string> CheckNames(List<string> nameList, List<string> regexList)
{
var missMatchNameList = new List<string>();
foreach (var name in nameList)
{
var matched = false;
foreach(var regex in regexList)
{
if (Regex.IsMatch(name, regex))
{
matched=true;
break;
}
}
if (!matched)
{
missMatchNameList.Add(name);
}
}
return missMatchNameList;
}
this issue because you break when not matching with first regex so you don't compare the remaining regexs

Find two strings in list with a regular expression

I need to find two strings within a list that contains the characters from another string, which are not in order. To make it clear, an example could be a list of animals like:
lion
dog
bear
cat
And a given string is: oodilgn.
The answer here would be: lion and dog
Each character from the string will be used only once.
Is there a regular expression that will allow me to do this?
You could try to put the given string between []. These brackets will allow choosing - in any order - from these letters only. This may not be a perfect solution, but it will catch the majority of your list.
For example, you could write oodilgn as [oodilgn], then add a minimum number of letters to be found - let's say 3 - by using the curly brackets {}. The full regex will be like this:
[oodilgn]{3,}
This code basically says: find any word that has three of the letters that are located between brackets in any order.
Demo: https://regex101.com/r/MCWHjQ/2
Here is some example algorithm that does the job. I have assumed that the two strings together don't need to take all letters from the text else i make additional commented check. Also i return first two appropriate answers.
Here is how you call it in the outside function, Main or else:
static void Main(string[] args)
{
var text = "oodilgn";
var listOfWords = new List<string> { "lion", "dog", "bear", "cat" };
ExtractWordsWithSameLetters(text, listOfWords);
}
Here is the function with the algorithm. All string manuplations are entirely with regex.
public static void ExtractWordsWithSameLetters(string text, List<string> listOfWords)
{
string firstWord = null;
string secondWord = null;
for (var i = 0; i < listOfWords.Count - 1; i++)
{
var textCopy = text;
var firstWordIsMatched = true;
foreach (var letter in listOfWords[i])
{
var pattern = $"(.*?)({letter})(.*?)";
var regex = new Regex(pattern);
if (regex.IsMatch(text))
{
textCopy = regex.Replace(textCopy, "$1*$3", 1);
}
else
{
firstWordIsMatched = false;
break;
}
}
if (!firstWordIsMatched)
{
continue;
}
firstWord = listOfWords[i];
for (var j = i + 1; j < listOfWords.Count; j++)
{
var secondWordIsMatched = true;
foreach (var letter in listOfWords[j])
{
var pattern = $"(.*?)({letter})(.*?)";
var regex = new Regex(pattern);
if (regex.IsMatch(text))
{
textCopy = regex.Replace(textCopy, "$1*$3", 1);
}
else
{
secondWordIsMatched = false;
break;
}
}
if (secondWordIsMatched)
{
secondWord = listOfWords[j];
break;
}
}
if (secondWord == null)
{
firstWord = null;
}
else
{
//if (textCopy.ToCharArray().Any(l => l != '*'))
//{
// break;
//}
break;
}
}
if (firstWord != null)
{
Console.WriteLine($"{firstWord} { secondWord}");
}
}
Function is far from optimised but does what you want. If you want to return results, not print them just create an array and stuff firstWord and secondWord in it and have return type string[] or add two paramaters with ref out In those cases you will need to check the result in the calling function.
please try this out
Regex r=new Regex("^[.*oodilgn]$");
var list=new List<String>(){"lion","dog","fish","god"};
var output=list.Where(x=>r.IsMatch(x));
result
output=["lion","dog","god"];

How to find match letter in (mystring after match letter)

I have need to check complex property in c#. I get the complex property List of string are:
EmployeeID
contactNo
Employee.FirstName // these is complex property
Employee.LastName // these is complex property
I know about regex.match() but i have doubt about how to check the string in after placed in dot value that means i want to check in Employee and after placed dot value. can you please help any idea about this?
Using regex, you can match complex properties like this:
List<string> properties = new List<string>()
{
"EmployeeID",
"contactNo",
"Employee.FirstName", // these is complex property
"Employee.LastName", // these is complex property
};
Regex rgx = new Regex(#"Employee\.(.*)");
var results = new List<string>();
foreach(var prop in properties)
{
foreach (var match in rgx.Matches(prop))
{
results.Add(match.ToString());
}
}
If you just want what is after the . (FirstName and LastName), replace the pattern like this:
Regex rgx = new Regex(#"(?<=Employee\.)\w*");
Without regex:
List<string> listofstring = { .... };
List<string> results = new List<string>();
const string toMatch = "Employee.";
foreach (string str in listofstring)
{
if (str.StartsWith(toMatch))
{
results.Add(str.Substring(toMatch.Length));
}
}
If you just need to match .
List<string> listofstring = { .... };
List<string> results = new List<string>();
const string toMatch = ".";
int index = -1;
foreach (string str in listofstring)
{
index = str.IndexOf(toMatch);
if(index >= 0)
{
results.Add(str.Substring(index + 1));
}
}

compare the characters in two strings

In C#, how do I compare the characters in two strings.
For example, let's say I have these two strings
"bc3231dsc" and "bc3462dsc"
How do I programically figure out the the strings
both start with "bc3" and end with "dsc"?
So the given would be two variables:
var1 = "bc3231dsc";
var2 = "bc3462dsc";
After comparing each characters from var1 to var2, I would want the output to be:
leftMatch = "bc3";
center1 = "231";
center2 = "462";
rightMatch = "dsc";
Conditions:
1. The strings will always be a length of 9 character.
2. The strings are not case sensitive.
The string class has 2 methods (StartsWith and Endwith) that you can use.
After reading your question and the already given answers i think there are some constraints are missing, which are maybe obvious to you, but not to the community. But maybe we can do a little guess work:
You'll have a bunch of string pairs that should be compared.
The two strings in each pair are of the same length or you are only interested by comparing the characters read simultaneously from left to right.
Get some kind of enumeration that tells me where each block starts and how long it is.
Due to the fact, that a string is only a enumeration of chars you could use LINQ here to get an idea of the matching characters like this:
private IEnumerable<bool> CommonChars(string first, string second)
{
if (first == null)
throw new ArgumentNullException("first");
if (second == null)
throw new ArgumentNullException("second");
var charsToCompare = first.Zip(second, (LeftChar, RightChar) => new { LeftChar, RightChar });
var matchingChars = charsToCompare.Select(pair => pair.LeftChar == pair.RightChar);
return matchingChars;
}
With this we can proceed and now find out how long each block of consecutive true and false flags are with this method:
private IEnumerable<Tuple<int, int>> Pack(IEnumerable<bool> source)
{
if (source == null)
throw new ArgumentNullException("source");
using (var iterator = source.GetEnumerator())
{
if (!iterator.MoveNext())
{
yield break;
}
bool current = iterator.Current;
int index = 0;
int length = 1;
while (iterator.MoveNext())
{
if(current != iterator.Current)
{
yield return Tuple.Create(index, length);
index += length;
length = 0;
}
current = iterator.Current;
length++;
}
yield return Tuple.Create(index, length);
}
}
Currently i don't know if there is an already existing LINQ function that provides the same functionality. As far as i have already read it should be possible with SelectMany() (cause in theory you can accomplish any LINQ task with this method), but as an adhoc implementation the above was easier (for me).
These functions could then be used in a way something like this:
var firstString = "bc3231dsc";
var secondString = "bc3462dsc";
var commonChars = CommonChars(firstString, secondString);
var packs = Pack(commonChars);
foreach (var item in packs)
{
Console.WriteLine("Left side: " + firstString.Substring(item.Item1, item.Item2));
Console.WriteLine("Right side: " + secondString.Substring(item.Item1, item.Item2));
Console.WriteLine();
}
Which would you then give this output:
Left side: bc3
Right side: bc3
Left side: 231
Right side: 462
Left side: dsc
Right side: dsc
The biggest drawback is in someway the usage of Tuple cause it leads to the ugly property names Item1 and Item2 which are far away from being instantly readable. But if it is really wanted you could introduce your own simple class holding two integers and has some rock-solid property names. Also currently the information is lost about if each block is shared by both strings or if they are different. But once again it should be fairly simply to get this information also into the tuple or your own class.
static void Main(string[] args)
{
string test1 = "bc3231dsc";
string tes2 = "bc3462dsc";
string firstmatch = GetMatch(test1, tes2, false);
string lasttmatch = GetMatch(test1, tes2, true);
string center1 = test1.Substring(firstmatch.Length, test1.Length -(firstmatch.Length + lasttmatch.Length)) ;
string center2 = test2.Substring(firstmatch.Length, test1.Length -(firstmatch.Length + lasttmatch.Length)) ;
}
public static string GetMatch(string fist, string second, bool isReverse)
{
if (isReverse)
{
fist = ReverseString(fist);
second = ReverseString(second);
}
StringBuilder builder = new StringBuilder();
char[] ar1 = fist.ToArray();
for (int i = 0; i < ar1.Length; i++)
{
if (fist.Length > i + 1 && ar1[i].Equals(second[i]))
{
builder.Append(ar1[i]);
}
else
{
break;
}
}
if (isReverse)
{
return ReverseString(builder.ToString());
}
return builder.ToString();
}
public static string ReverseString(string s)
{
char[] arr = s.ToCharArray();
Array.Reverse(arr);
return new string(arr);
}
Pseudo code of what you need..
int stringpos = 0
string resultstart = ""
while not end of string (either of the two)
{
if string1.substr(stringpos) == string1.substr(stringpos)
resultstart =resultstart + string1.substr(stringpos)
else
exit while
}
resultstart has you start string.. you can do the same going backwards...
Another solution you can use is Regular Expressions.
Regex re = new Regex("^bc3.*?dsc$");
String first = "bc3231dsc";
if(re.IsMatch(first)) {
//Act accordingly...
}
This gives you more flexibility when matching. The pattern above matches any string that starts in bc3 and ends in dsc with anything between except a linefeed. By changing .*? to \d, you could specify that you only want digits between the two fields. From there, the possibilities are endless.
using System;
using System.Text.RegularExpressions;
using System.Collections.Generic;
class Sample {
static public void Main(){
string s1 = "bc3231dsc";
string s2 = "bc3462dsc";
List<string> common_str = commonStrings(s1,s2);
foreach ( var s in common_str)
Console.WriteLine(s);
}
static public List<string> commonStrings(string s1, string s2){
int len = s1.Length;
char [] match_chars = new char[len];
for(var i = 0; i < len ; ++i)
match_chars[i] = (Char.ToLower(s1[i])==Char.ToLower(s2[i]))? '#' : '_';
string pat = new String(match_chars);
Regex regex = new Regex("(#+)", RegexOptions.Compiled);
List<string> result = new List<string>();
foreach (Match match in regex.Matches(pat))
result.Add(s1.Substring(match.Index, match.Length));
return result;
}
}
for UPDATE CONDITION
using System;
class Sample {
static public void Main(){
string s1 = "bc3231dsc";
string s2 = "bc3462dsc";
int len = 9;//s1.Length;//cond.1)
int l_pos = 0;
int r_pos = len;
for(int i=0;i<len && Char.ToLower(s1[i])==Char.ToLower(s2[i]);++i){
++l_pos;
}
for(int i=len-1;i>0 && Char.ToLower(s1[i])==Char.ToLower(s2[i]);--i){
--r_pos;
}
string leftMatch = s1.Substring(0,l_pos);
string center1 = s1.Substring(l_pos, r_pos - l_pos);
string center2 = s2.Substring(l_pos, r_pos - l_pos);
string rightMatch = s1.Substring(r_pos);
Console.Write(
"leftMatch = \"{0}\"\n" +
"center1 = \"{1}\"\n" +
"center2 = \"{2}\"\n" +
"rightMatch = \"{3}\"\n",leftMatch, center1, center2, rightMatch);
}
}

Categories