I have a code for conversion but some address have different result to what is expected.
23 Starling St => 3 Streetarling Street which is wrong and it should be 23 Starling Street
1 St Johns Ct => 1 Street Johns Ct => Correct
This is the code:
private string StreetConversion(string address, Order order)
{
string[] addressList = address.Split(' ');
foreach (string add in addressList)
{
if(add == "pde")
address = address.Replace("pde", "Parade");
if (add == "Pde")
address = address.Replace("Pde", "Parade");
if (add == "Rd")
address = address.Replace("Rd", "Road");
if (add == "rd")
address = address.Replace("rd", "Road");
if (add == "St")
address = address.Replace("St", "Street");
if (add == "st")
address = address.Replace("st", "Street");
}
order.ShipAddress1 = address;
return address;
}
You need to replace given word instead of replacing all occurences of that word in address variable,
private string StreetConversion(string address, Order order)
{
string[] addressList = address.Split(' ');
StringBuilder newAddress = new StringBuilder();
foreach (string add in addressList)
{
if(add.ToLower() == "pde")
newAddress.Append("Parade ");
else if (add.ToLower() == "rd")
newAddress.Append("Road ");
else if (add.ToLower() == "st")
newAddress.Append("Street ");
else
newAddress.Append(add+ " ");
}
order.ShipAddress1 = newAddress.ToString();
return newAddress.ToString();
}
First of all, let's extract model: acronyms and their substitutions
private static Dictionary<string, string> acronyms =
new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase) {
{"pde", "Parade"},
{"rd", "Road"},
{"st", "Street"},
//TODO: Add more pairs if required, say, {"sq", "square"},
};
Then we can easily transform the string address:
using System.Linq;
...
private string StreetConversion(string address, Order order) {
string result = string.Join(" ", address
.Split(' ')
.Select(word => acronyms.TryGetValue(word, out var newWord) ? newWord : word));
order.ShipAddress1 = result;
return result;
}
Step 1: Lets follow "The Single Responsibility Principle" to keep code cleaner and maintainable.
"The Single Responsibility Principle: A class or method should have only a single responsibility."
So you need to take out the following code from the method:
order.ShipAddress1 = address;
Step 2: Your method responsibility should only be converting the street and return it back, and you can reach that in many ways, the following is one of the ways:
private string StreetConversion(string address) {
var newAddress= string.Empty;
foreach (var s in address.Split(' ')) {
newAddress+= (s.ToLower()) switch {
"st" => "Street",
"rd" => "Road",
"pde" => "Parade",
_ => s + " ",
};
}
return newAddress;
}
Step 3: Calling the method
order.ShipAddress1 = StreetConversion(OldAddress);
You can use Regex also to replace the work with another word.
s = Regex.Replace(s, #"\boldVal1\b", "newVal1", RegexOptions.IgnoreCase);
s = Regex.Replace(s, #"\boldVal2\b", "newVal2", RegexOptions.IgnoreCase);
Related
I want to read settings from a text file.
string MySettingsFile = "Settings.txt";
Normally, the file would have 4 lines.
SettingA:Alpha1
SettingB:Bravo2
SettingC:Charlie1
SettingD:Delta6
I want to get each line in it's own variable, like this :
string MyAlpa = "Alpha1";
string MyBravo = "Bravo2";
string MyCharlie = "Charlie1";
string MyDelta = "Delta6";
Normally, I would just read the lines in a loop, reading each line and setting the string as I go.
If, however, Line 4 is missing, and I am looking for the part of the line after the colon, I get an error if I check for it like this...
MyDelta = MyDeltaSubstring(MyDelta.LastIndexOf(':') + 1);
Is there a way to 'check for the existence of a specific line' before I attempt to get the SubString (so it doesn't get an error), like in
a function separate that has try, catch, finally with return either the string I want or the word "Missing", if that line is missing (and then stop since there are no more lines)?
function DoesLineExist(int X, string myFile)
{
string MyString;
try ()
{
// read line X from file myFile
// get everything AFTER the ":" and put it in MyString
// ??? //
}
catch (ArgumentException null)
{
MyString = "Missing";
}
catch (ArgumentException e)
{
MyString = "Missing";
}
finally
{
MyString = ? // whatever we got From Line X (after ":")
}
return MyString; // only if line is missing
}
Is there a better way of doing this?
ReadAllLines or something maybe?
You need to first verify that that line is exist or not and then again check that is line contains key/value pair of settings is exist and then project your key value pair into dictionary and then get each setting to your variable by its key name.
Here i create a console app for your demonstration purpose.
class Program
{
static void Main(string[] args)
{
Dictionary<string, string> dictSettings = new Dictionary<string, string>();
string MyAlpa = "";
string MyBravo = "";
string MyCharlie = "";
string MyDelta = "";
var lines = File.ReadAllLines(#"C:\Users\xxx\source\repos\ConsoleApp4\ConsoleApp4\Files\Sample.txt");
for (var i = 0; i < lines.Length; i += 1)
{
var line = lines[i];
//DoesLineExist(line);
if (!string.IsNullOrEmpty(line) && line.Contains(":"))
{
string settingKey = line.Split(':')[0];
string settingValue = line.Split(':')[1];
dictSettings.Add(settingKey, settingValue);
}
}
MyAlpa = dictSettings.ContainsKey("SettingA") ? dictSettings["SettingA"] : "";
MyBravo = dictSettings.ContainsKey("SettingB") ? dictSettings["SettingB"] : "";
MyCharlie = dictSettings.ContainsKey("SettingC") ? dictSettings["SettingC"] : "";
MyDelta = dictSettings.ContainsKey("SettingD") ? dictSettings["SettingD"] : "";
Console.WriteLine(MyAlpa);
Console.WriteLine(MyBravo);
Console.WriteLine(MyCharlie);
Console.WriteLine(MyDelta);
Console.ReadLine();
}
//private static void DoesLineExist(string line)
//{
// if (!string.IsNullOrEmpty(line) && line.Contains(":"))
// {
// string settingKey = line.Split(':')[0];
// string settingValue = line.Split(':')[1];
// dictSettings.Add(settingKey, settingValue);
// }
//}
}
Input:
SettingA:Alpha1
SettingB:Bravo2
SettingC:Charlie1
Output:
Input:
SettingA:Alpha1
SettingC:Charlie1
SettingD:
Output:
Here is few different way to build the dictionary or list object we will use later. The choice is simple are those key unique or do you have multiple value for some SettingB. If the relation is one-one a Dictionary could be a solution. Giving you access to method like ContainsKey
var regexDictionary = Regex.Matches( File.ReadAllText(path)
, "(?<key>.+):(?<value>.+)")
.OfType<Match>()
.Where(m => m.Success)
.ToDictionary(m => m.Groups["key"].Value.Trim(),
m => m.Groups["value"].Value.Trim());
var ObjList = File.ReadAllLines(path)
.Select(line => line.Split(':'))
.Select(x => new MyObject {
prop1 = x[0],
prop2 = x[1]
// etc
})
var linQDictionary = File.ReadAllLines(path)
.Select(line => line.Split(':'))
.ToDictionary(
c => x[0],
c => x[1]
);
Does the key exist in the dictionary?
if (!dictionary .ContainsKey("SettingB"))
{
Console.WriteLine("For key = \"SettingB\", value = {0}.", dictionary["SettingB"]);
}
In a list of object:
if (ObjList .Any(x=> x.prop1 == "SettingZ" ))
{
// Select the object.
}
My input is going to be as follows:
abc#gmail.com,def#yahoo.com;xyz#gmail.com;ghi#hotmail.com and so on
Now I want my output to be:
abc
def
xyz
ghi
The following is my code:
using System;
using System.Text.RegularExpressions;
public class Program
{
public static void Main(string[] args)
{
string str;
string[] newstr,newstr2;
Console.WriteLine("Enter the email addresses: ");
str=Console.ReadLine();
newstr=Regex.Split(str,",|;|#");
foreach (string s in newstr)
{
Console.WriteLine(s);
}
}
}
My output right now is:
abc
gmail.com
def
yahoo.com
xyz
gmail.com
ghi
hotmail.com
Any kind of help would be greatly appreciated. Thanks.
You shouldn't use regex for split, and should no split by #. Instead, use the follopwing code:
using System;
public class Program
{
public static void Main(string[] args)
{
string str;
string[] newstr;
Console.WriteLine("Enter the email addresses: ");
str = Console.ReadLine();
newstr = str.Split(new char[] { ',', ';' }); // Split to get a temporal array of addresses
foreach (string s in newstr)
{
Console.WriteLine(s.Substring(0, s.IndexOf('#'))); // Extract the sender from the email addresses
}
}
}
Edit:
Or, with LINQ:
using System;
using System.Linq;
public class Program
{
public static void Main(string[] args)
{
string str;
string[] newstr;
Console.WriteLine("Enter the email addresses: ");
str = Console.ReadLine();
newstr = str.Split(new char[] { ',', ';' }) // Split to get a array of addresses to work with
.Select(s => s.Substring(0, s.IndexOf('#'))).ToArray(); // Extract the sender from the email addresses
foreach (string s in newstr)
{
Console.WriteLine(s);
}
}
}
another approach without RegEx
string input = "abc#gmail.com,def#yahoo.com;xy#gmail.com; ghi#hotmail.com";
var result = input.Split(',', ';').Select(x => x.Split('#').First());
first Split the adresses by , and ;, then select the part before the # by splitting again.
You can use this email regex:
var regex = new Regex(#"(?<name>\w+([-+.']\w+)*)#\w+([-.]\w+)*\.\w+([-.]\w+)*");
var results =
regex.Matches("abc#gmail.com,def#yahoo.com;xyz#gmail.com;ghi#hotmail.com")
.Cast<Match>()
.Select(m => m.Groups["name"].Value)
.ToList();
Perhaps using this might help
str.Substring(0, str.LastIndexOf(" ")<0?0:str.LastIndexOf(" "));
As Mail is a weird thing with a complexe definition, I will never assume that something with an # is a mail.
My best try would be to convert the string to a MailAddress, just in case it look like a mail but it's not one because of some invalid char etc.
string input = "abc#gmail.com,ghi#hotmail.com;notme; #op this is not a mail!";
var result = input
.Split(',', ';') // Split
.Select(x =>
{
string adr = "";
try
{ // Create an MailAddress, MailAddress has no TryParse.
adr = new MailAddress(x).User;
}
catch
{
return new { isValid = false, mail = adr };
}
return new { isValid = true, mail = adr };
})
.Where(x => x.isValid)
.Select(x => x.mail);
Actually, in the regular expression, to capture some substring, you need to wrap the expected content by ( and )
Below code should work
string str22 = "abc#gmail.com;def#yahoo.com,xyz#gmail.com;fah#yao.com,h347.2162#yahoo.com.hk";// ghi#hotmail.com";
List<string> ret = new List<string>();
string regExp = #"(.*?)#.*?[,;]{1}|(.*)#";
MatchCollection matches = Regex.Matches(str22, regExp, RegexOptions.IgnoreCase);
foreach (Match match in matches)
{
if (match.Success)
{
int pvt = 1;
while (string.IsNullOrEmpty(match.Groups[pvt].Value))
{
pvt++;
}
MessageBox.Show(match.Groups[pvt].Value);
}
}
return;
The regular expression is as below
(.*?)#.*?[,;]{1}|(.*)#
(.*?)#.*?[,;]{1} is fetching the substring before # and ? restrict it fetches the first match.
The last email do not contain , or ;, thus add a OR condition and fetch the last email name by the substring before #
I have two strings like this:
string longName = "PRODUCT MANAGER OFFICE";
string shortName = "P.M.O";
I want to validate if longName contains short name alphabetic characters (PMO).
But the short name may be like this:
string shortName = "PMO";
string shortName = "pmo";
string shortName = "pmoff";
All of these short names contain p, m, o, f characters and these are also used in long name.
So I want to math string characters. How can I do this? Should I use regex or another way?
Regex is to heavy and slow for this simple simple task.
string longName = "PRODUCT MANAGER OFFICE";
string shortName = "P.M.O";
public bool ValidateStrings(string longName, string shortName)
{
bool isValid = false;
foreach (var character in shortName)
{
if (Char.IsLetter(character))
{
isValid = longName.Contains<char>(character);
if (!isValid)
{
return false;
}
}
}
return isValid;
}
It's quite easy. Firstly, split the entire string into words :
var text = "PRODUCT MANAGER OFFICE";
string Shortform = "";
var words = text.Split(' ').ToArray();
foreach (string word in words)
{
///Now in the loop, we will get the first character of each word and pass it to the ShortForm variable
Shortform = Shortform + word.Substring(0,1);
}
So, now you have the short form as well, you can easily compare it with the given short form, sample :
if(ShortForm == "abc")
{
}
Hope this helps :)
Try this code...
string str = "PRODUCT MANAGER OFFICE";
string shortname = "P.M.O";
string compareStr = "";
string[] strArry = str.Split(' ');
foreach (string x in strArry)
{
if(x.Trim() != string.Empty)
{
compareStr += x[0];
}
}
//Console.WriteLine(compareStr);
if(compareStr.Equals((shortname.Replace(".", string.Empty)), StringComparison.InvariantCultureIgnoreCase))
{
Console.WriteLine("valid");
}
Demo 01: http://rextester.com/XVCG27648
Demo 02: http://rextester.com/GLSN75240
Demo 03: http://rextester.com/KVTN19152
The above method will not work if you enter 'PMOFF'. If you also want that, you can check the below method. Keep in mind that these both solutions only check the first character of all words. The second method will just ignore other characters.
string str = "PRODUCT MANAGER OFFICE";
string shortname = "P.M.OFF";
int count = 0;
string compareStr = "";
string[] strArry = str.Split(' ');
foreach (string x in strArry)
{
if(x.Trim() != string.Empty)
{
count++;
compareStr += x[0];
}
}
if(compareStr.Equals((((compareStr + " ").Remove(count)).Replace(".", string.Empty)), StringComparison.InvariantCultureIgnoreCase))
{
Console.WriteLine("valid");
}
Demo 04: http://rextester.com/EUIJ88090
It looks like you're matching up abbreviated names to proper names but without any real formatting (PRODUCT MANAGER OFFICE can be pmo or pmoff). So you need to be able to search each character for a match and stop searching when no match is found. You also need to keep track of the last position so that subsequent duplicate characters must match a new position. This makes the assumption that the order matters (pmo matches PRODUCT MANAGER OFFICE but won't match PRODUCT OFFICE MANAGER) and that all characters matter (pmoff matches PRODUCT MANAGER OFFICE but won't match PRODUCT MANAGER OF)
The following will work for all of your sample inputs and some additional test cases for longNames that I added. It's still not bulletproof though. As you can see with these requirements pmo will also match GYPSUM MINING OFFICE because of the P in the first word. Maybe you can tailor it to your needs.
static void Main()
{
var longNames = new string[]
{
"PRODUCT MANAGER OFFICE",
"GYPSUM MINING OFFICE",
"The PRODUCTION of MANAGEMENT for OFFICES",
"PRODUCT PACKING PLANT",
"PRODUCT OFFICE MANAGER",
"PRODUCT MANAGER OF"
};
string shortName = "P.M.Off";
var charsToSearchFor = shortName.ToLower().Where(c => char.IsLetter(c)).ToArray();
foreach (var longName in longNames.Select(longName=>longName.ToLower()))
{
var allCharsFound = true;
var lastMatchPosition = -1;
foreach (var searchChar in charsToSearchFor)
{
var matchPosition = longName.IndexOf(searchChar, lastMatchPosition+1);
if (matchPosition > lastMatchPosition)
{
lastMatchPosition = matchPosition;
}
else if (matchPosition == -1)
{
allCharsFound = false;
break;
}
}
Console.WriteLine($"{longName} : {allCharsFound}");
}
Console.ReadKey();
}
Using P.M.Off as the shortName input, this will output:
PRODUCT MANAGER OFFICE : True
GYPSUM MINING OFFICE : True
The PRODUCTION of MANAGEMENT for OFFICES : True
PRODUCT PACKING PLANT : False
PRODUCT OFFICE MANAGER : False
PRODUCT MANAGER OF: False
I want to search a keyword by start with any word of any column.
For example displayedCustomers object contain Name which is Sameer Singh.
So when my search variable
searchOption="eer" //it should not search,
searchOption="ingh" //it should not search,
searchOption="Sa" //it should search,
searchOption="Si" //it should search,
searchOption="ameer" //it should not search
I am using this code previously for whole word.But don't know to split space of string and compare with starting element. Please help to do this in efficient way
// Split the word by space
var split = str.Split(" ");
// Check if firstname or lastname starts with searchString
var found = split[0].StartsWith(searchString) || split[1].StartsWith(searchString);
If you dont know if the person has a middle name as well, and you wish to test on that as well:
var searchString = "Sam";
var split = customerName.Split(" ");
var found = false;
foreach(var str in split)
{
found == found || str.StartsWith(searchString);
if(found)
break;
}
Wrapping this up in a method:
public bool NameStartsWith(string name,string searchStr)
{
var split = name.Split(" ");
foreach(var str in split)
{
if(str.StartsWith(searchString))
return true;
}
return false;
}
Use it like this:
var matches = NameStartsWith("Sameer Singh","Sa"); // true
var matches = NameStartsWith("Sameer Singh","Si"); // true
var matches = NameStartsWith("Sameer Singh","S"); // true
var matches = NameStartsWith("Bobby Singer Bobberson","Sing"); // true
var matches = NameStartsWith("Sameer Singh","meer"); // false
You can use String.StartsWith
string name = "Sameer Singh";
string searchOption = "eer";
bool nameStartsWith = name.StartsWith(searchOption);
Console.Write("{0} {1} {2}"
, name
, nameStartsWith ? "starts with" : "starts not with"
, searchOption);
Demo: http://ideone.com/mEh5Q1
You can do that for every word or every column in your record.
For example(assuming DataRow):
bool rowContains = row.ItemArray.Any(o => string.Format("{0}", o).StartsWith(searchOption));
assuming String[]:
bool arrContains = array.Any(str => str.StartsWith(searchOption));
assuming String:
bool nameContains = name.Split().Any(w => w.StartsWith(searchOption));
You should use String.StartsWith together with String.Split:
public bool IsMatching(string Name, string SearchOption)
{
foreach (string s in Name.Split(' '))
{
if s.StartsWith(SearchOption)
return true;
}
return false;
}
// use it like:
if IsMatching("Sameer Singh", "Sa")
{
// ...
In many places in our code we have collections of objects, from which we need to create a comma-separated list. The type of collection varies: it may be a DataTable from which we need a certain column, or a List<Customer>, etc.
Now we loop through the collection and use string concatenation, for example:
string text = "";
string separator = "";
foreach (DataRow row in table.Rows)
{
text += separator + row["title"];
separator = ", ";
}
Is there a better pattern for this? Ideally I would like an approach we could reuse by just sending in a function to get the right field/property/column from each object.
string.Join(", ", Array.ConvertAll(somelist.ToArray(), i => i.ToString()))
static string ToCsv<T>(IEnumerable<T> things, Func<T, string> toStringMethod)
{
StringBuilder sb = new StringBuilder();
foreach (T thing in things)
sb.Append(toStringMethod(thing)).Append(',');
return sb.ToString(0, sb.Length - 1); //remove trailing ,
}
Use like this:
DataTable dt = ...; //datatable with some data
Console.WriteLine(ToCsv(dt.Rows, row => row["ColName"]));
or:
List<Customer> customers = ...; //assume Customer has a Name property
Console.WriteLine(ToCsv(customers, c => c.Name));
I don't have a compiler to hand but in theory it should work. And as everyone knows, in theory, practice and theory are the same. In practice, they're not.
I found string.Join and lambda Select<Func<>> helps to write minimum code.
List<string> fruits = new List<string>();
fruits.Add("Mango");
fruits.Add("Banana");
fruits.Add("Papaya");
string commaSepFruits = string.Join(",", fruits.Select(f => "'" + f + "'"));
Console.WriteLine(commaSepFruits);
List<int> ids = new List<int>();
ids.Add(1001);
ids.Add(1002);
ids.Add(1003);
string commaSepIds = string.Join(",", ids);
Console.WriteLine(commaSepIds);
List<Customer> customers = new List<Customer>();
customers.Add(new Customer { Id = 10001, Name = "John" });
customers.Add(new Customer { Id = 10002, Name = "Robert" });
customers.Add(new Customer { Id = 10002, Name = "Ryan" });
string commaSepCustIds = string.Join(", ", customers.Select(cust => cust.Id));
string commaSepCustNames = string.Join(", ", customers.Select(cust => "'" + cust.Name + "'"));
Console.WriteLine(commaSepCustIds);
Console.WriteLine(commaSepCustNames);
Console.ReadLine();
// using System.Collections;
// using System.Collections.Generic;
// using System.Linq
public delegate string Indexer<T>(T obj);
public static string concatenate<T>(IEnumerable<T> collection, Indexer<T> indexer, char separator)
{
StringBuilder sb = new StringBuilder();
foreach (T t in collection) sb.Append(indexer(t)).Append(separator);
return sb.Remove(sb.Length - 1, 1).ToString();
}
// version for non-generic collections
public static string concatenate<T>(IEnumerable collection, Indexer<T> indexer, char separator)
{
StringBuilder sb = new StringBuilder();
foreach (object t in collection) sb.Append(indexer((T)t)).Append(separator);
return sb.Remove(sb.Length - 1, 1).ToString();
}
// example 1: simple int list
string getAllInts(IEnumerable<int> listOfInts)
{
return concatenate<int>(listOfInts, Convert.ToString, ',');
}
// example 2: DataTable.Rows
string getTitle(DataRow row) { return row["title"].ToString(); }
string getAllTitles(DataTable table)
{
return concatenate<DataRow>(table.Rows, getTitle, '\n');
}
// example 3: DataTable.Rows without Indexer function
string getAllTitles(DataTable table)
{
return concatenate<DataRow>(table.Rows, r => r["title"].ToString(), '\n');
}
In .NET 4 you can just do string.Join(", ", table.Rows.Select(r => r["title"]))
You could write a function that transforms a IEnumerable<string> into a comma-separated string:
public string Concat(IEnumerable<string> stringList)
{
StringBuilder textBuilder = new StringBuilder();
string separator = String.Empty;
foreach(string item in stringList)
{
textBuilder.Append(separator);
textBuilder.Append(item);
separator = ", ";
}
return textBuilder.ToString();
}
You can then use LINQ to query your collection/dataset/etc to provide the stringList.
As an aside: The first modification I would make is to use the StringBuilder Class instead of just a String - it'll save resources for you.
I love Matt Howells answer in this post:
I had to make it into an extension:
public static string ToCsv<T>(this IEnumerable<T> things, Func<T, string> toStringMethod)
Usage (I am getting all the emails and turning them into a CSV string for emails):
var list = Session.Find("from User u where u.IsActive = true").Cast<User>();
return list.ToCsv(i => i.Email);
For collections you can use this method as well, for example:
string.Join(", ", contactsCollection.Select(i => i.FirstName));
You can select any property that you want to separate.
string strTest = "1,2,4,6";
string[] Nums = strTest.Split(',');
Console.Write(Nums.Aggregate<string>((first, second) => first + "," + second));
//OUTPUT:
//1,2,4,6
Here's my favorite answer adapted to the question,
and corrected Convert to ConvertAll:
string text = string.Join(", ", Array.ConvertAll(table.Rows.ToArray(), i => i["title"]));