I have to parse a file that is constructed like this :
User: jcruz Name: Jules Last: Cruz Email: Some#email.com
User: jdoe Name: John Last: Doe Email: Some#email.com
User: pmartin Name: Pete Last: Martin Email: Some#email.com
User: rrichard Name: Reed Last: Richard Email: Some#email.com
I need to split every line taking just Name, Last Name and Email into an object of the type
var contact = new Conctact {
Name = fieldFromLine,
Last= fieldFromLine,
Email = fieldFromLine
}
So my problem is which tool use : String.Split or Regex.Split. and how to implement it.
Thank you very much...
This is what a Have done so far:
String archivo = ((FileDialog)sender).FileName;
using (TextReader sr = new StreamReader(archivo,Encoding.UTF8))
{
String line = String.Empty;
while ((line = sr.ReadLine()) != null )
{
string[] result = Regex.Split(line,"User:");
//How to get the other fields...
}
}
var result =File.ReadLines(fileName)
.Select(line => line.Split(new string[]{"User:", "Name:", "Last:", "Email:"}, StringSplitOptions.RemoveEmptyEntries))
.Select(parts => new Conctact(){ Name = parts[1], Last = parts[2], Email = parts[3] })
.ToArray();
try this:
public class contact
{
public string Name { get; set; }
public string Lname { get; set; }
public string Email { get; set; }
}
List<contact> contact = new List<contact>();
private void split()
{
var lines = File.ReadAllLines(#"txt file address");
foreach (var line in lines)
{
var splitline=line.Split(':');
string name = splitline[2].Replace("Last", "");
string lname = splitline[3].Replace("Email","");
contact.Add(new contact { Name = name, Lname = lname, Email = splitline[4] });
}
}
Answer: neither.
Use a simple finite-state machine parser to read the file because unless you can guarantee that the text values will never be "Name:" or "Last:" or "Email:" then you'll run into problems with string splitting. Also FSM-based parsers are significantly faster than string splitting (as there is no extraneous string allocation).
I don't have the time to write out an entire parser, but here's the simple logic:
enum State { InUser, InName, InLast, InEmail }
State currentState = State.InUser; // you start off with the 'cursor' in the "User" section
StringBuilder sb = new StringBuilder(); // this holds the current string element
foreach(Char c in entireTextFile) { // presumably using `StreamReader.Read()`
switch( currentState ) {
case InUser:
switch( c ) {
// state transition logic here
}
// append the character to the StringBuilder until you've identified and reached the next field, then save the sb value to the appropriat
case InName:
// and so on...
}
}
Of course, an FSM parser is fundamentally the same thing as a Regular Expression parser, but it means you get to code the state-transitions yourself rather than using RegEx's syntax which is faster, performance-wise.
If your project is small and don't care about performance, and can guarantee certain data formatting rules then I'd go with regex.
But never, ever, use String.Split to read a file.
Regex is overkill. Also note that some last names that contain spaces.
Contact c = new Contact();
string () tokens = input.Split(":".ToCharArray());
if (tokens.Count < 5)
return; // error
// now strip the last word from each token
c.Name = tokens(2).Substring(0, tokens(2).LastIndexOf(" ".ToCharArray())).Trim();
c.Last = tokens(3).Substring(0, tokens(3).LastIndexOf(" ".ToCharArray())).Trim();
c.Email = tokens(4).Trim();
Related
I want to read a textfile dynamically based on the headers. Consider an example like this
name|email|phone|othername|company
john|john#example.com|1234||example
doe|doe#example.com||pin
jane||98485|
The values to be read like this for the following records
name email phone othername company
john john#example.com 1234 example
doe doe#example.com pin
jane 98485
I tried using this
using (StreamReader sr = new StreamReader(new MemoryStream(textFile)))
{
while (sr.Peek() >= 0)
{
string line = sr.ReadLine(); //Using readline method to read text file.
string[] strlist = line.Split('|'); //using string.split() method to split the string.
Obj obj = new Obj();
obj.Name = strlist[0].ToString();
obj.Email = strlist[1].ToString();
obj.Phone = strlist[2].ToString();
obj.othername = strlist[3].ToString();
obj.company = strlist[4].ToString();
}
}
Above code works if all the delimiters are put exactly but doesn't work when given dynamically like the above. Any possible solution for this?
If you have any control over this, you should use a better serialization techinology, or at least use a csv parser that can deal with this sort of format. However, if you want to use string.Split, you can also take advantage of ElementAtOrDefault
Returns the element at a specified index in a sequence or a default
value if the index is out of range.
Given
public class Data
{
public string Name { get; set; }
public string Email { get; set; }
public string Phone { get; set; }
public string OtherName { get; set; }
public string Company { get; set; }
}
Usage
var results = File
.ReadLines(SomeFileName) // stream the lines from a file
.Skip(1) // skip the header
.Select(line => line.Split('|')) // split on pipe
.Select(items => new Data() // populate some funky class
{
Name = items.ElementAtOrDefault(0),
Email = items.ElementAtOrDefault(1),
Phone = items.ElementAtOrDefault(2),
OtherName = items.ElementAtOrDefault(3),
Company = items.ElementAtOrDefault(4)
});
foreach (var result in results)
Console.WriteLine($"{result.Name}, {result.Email}, {result.Phone}, {result.OtherName}, {result.Company}");
Output
john, john#example.com, 1234, , example
doe, doe#example.com, , pin,
jane, , 98485, ,
When you split the line like string[] strlist = line.Split('|'); you can get undesired results.
For example: jane||98485| generates an array of just 4 elements as you can check here https://rextester.com/WBOT6074 online.
You should check your array strList after generating it with thinks like measuring the size.
As you haven't given clear details about the problem I cannot give a more especific answer to it.
I need to open a csv file. Than I need filter each data and generate an output for each value of them.
◘ Example
•Input file = "full list.csv"
NAME CITY
Mark Venezia
John New York
Lisa San Miguel
Emily New York
Amelia New York
Nicolas Venezia
Bill San Miguel
Steve Venezia
Output will be =
• file1 = "full list_Venezia.csv"
NAME CITY
Mark Venezia
Nicolas Venezia
Steve Venezia
• file2 = "full list_New York.csv"
NAME CITY
John New York
Emily New York
Amelia New York
• file3 = "full list_San Miguel"
NAME CITY
Lisa San Miguel
Bill San Miguel
I'm using c# with ConsoleApplication on Visual Studio and I started to read the input file in this method:
string inputFile = "full list.csv";
string outputFile;
string line;
string titles = File.ReadLines(inputFile).First();
System.IO.StreamReader file = new System.IO.StreamReader(inputFile);
while ((line = file.ReadLine()) != null)
{
}
file.Close();
System.IO.StreamWriter fileOut = new System.IO.StreamWriter(outputFile);
foreach (DatiOutput objOut in listOutput)
{
}
fileOut.Close();
Is there an algorithm that allows me to filter the data I need?
Here's a non-LINQy approach using a Dictionary to keep a reference to each output file based on the city name as the Key (there's nothing wrong with LINQ, though!):
string[] values;
string header;
string line, city, outputFileName;
string inputFile = "full list.csv";
Dictionary<string, System.IO.StreamWriter> outputFiles = new Dictionary<string, System.IO.StreamWriter>();
using (System.IO.StreamReader file = new System.IO.StreamReader(inputFile))
{
header = file.ReadLine();
while ((line = file.ReadLine()) != null)
{
values = line.Split(",".ToCharArray());
city = values[1];
if (!outputFiles.ContainsKey(city))
{
outputFileName = "full list_" + city + ".csv";
outputFiles.Add(city, new System.IO.StreamWriter(outputFileName));
outputFiles[city].WriteLine(header);
}
outputFiles[city].WriteLine(line);
}
}
foreach(System.IO.StreamWriter outputFile in outputFiles.Values)
{
outputFile.Close();
}
You have written most of the good parts yourself, and now you need to fill the blanks.
Breaking down the steps
Read the CSV to a Collection
Group Collection based on City
Write the
each group to separate file
The first step is of course is to read the input file
var listOutput = new List<DatiOutput>();
while ((line = file.ReadLine()) != null)
{
var data = line.Split(new []{";"},StringSplitOptions.RemoveEmptyEntries);
if(!data[0].Trim().Equals("NAME"))
listOutput.Add(new DatiOutput{ Name = data[0].Trim(), City = data[1].Trim()});
}
I have assumed your DatiOutput looks like following as it was not given.
public class DatiOutput
{
public string City{get;set;}
public string Name{get;set;}
}
Then next step is to Group the collection based on City and then write them to file. You can use LINQ to group the collection based on City.
listOutput.GroupBy(c=>c.City)
Once your have the result, you can now create file name with corresponding city name appended, and add the data to it.
foreach (var objOut in listOutput.GroupBy(c=>c.City))
{
var filePath = $"{Path.Combine(Path.GetDirectoryName(inputFile),Path.GetFileNameWithoutExtension(inputFile))}_{objOut.First().City}.csv";
using(System.IO.StreamWriter fileOut = new System.IO.StreamWriter(File.Open(filePath, FileMode.OpenOrCreate, FileAccess.ReadWrite)))
{
fileOut.WriteLine($"NAME;CITY");
foreach(var items in objOut)
{
fileOut.WriteLine($"{items.Name};{items.City}");
}
}
}
You would have the desired result
foreach (var g in File.ReadAllLines("full list.csv")
.Skip(1)
.Select(l => new {
Name = l.Substring(0, l.IndexOf(',')),
City = l.Substring(l.IndexOf(',') + 1) })
.GroupBy(l => l.City))
{
File.WriteAllLines($"full list_{g.Key}.csv", new[] { "NAME,CITY" }
.Concat(g.Select(l => $"{l.Name},{l.City}")));
}
The key part your example was missing was GroupBy - this allows you to group the data you have read in to groups based on a certain criteria (in our case City).
Group by is a powerful LINQ extension that allows you to filter data. The example above reads in all the data, skips the header, uses select to transform each line into an instance of an anonymous type to contain the name and city. GroupBy is then used to group these instances by city. Then for each group the data is written to a new file.
I would take #TVOHMs answer to slightly cleaner direction by keeping the same codestyle on the whole solution.
File.ReadAllLines("full list.csv") // Read the input file
.Skip(1) // Skip the header row
.Select(row => row.Split(',')) // Split each row to array of city and name
.GroupBy(row => row[1], row => row[0]) // Group by cities, selecting names
.ToList() // To list, so .ForEach is possible
.ForEach(group => File.WriteAllLines($"full list_{group.Key}.csv", group)); // Create file for each group and write the names
I am implementing a integration with NetSuite in C#. In the external system I need to populate a list of countries that will match NetSuite's country list.
The NetSuite Web Service provides an enumeration call Country
public enum Country {
_afghanistan,
_alandIslands,
_albania,
_algeria,
...
You can also get a list of country Name and Code (in an albeit not so straight forward way) from the web service. (See: http://suiteweekly.com/2015/07/netsuite-get-all-country-list/)
Which gives you access to values like this:
Afghanistan, AF
Aland Islands, AX
Albania, AL
Algeria, DZ
American Samoa, AS
...
But, as you can see, there is no way to link the two together. (I tried to match by index but that didn't work and sounds scary anyway)
NetSuite's "help" files have a list. But this is static and I really want a dynamic solution that updates as NetSuites updates because we know countries will change--even is not that often.
Screenshot of Country Enumerations from NetSuite help docs
The only solutions I have found online are people who have provided static data that maps the two sets of data. (ex. suiteweekly.com /2015/07/netsuite-complete-country-list-in-netsuite/)
I cannot (don't want to) believe that this is the only solution.
Anyone else have experience with this that has a better solution?
NetSuite, if you are reading, come on guys, give a programmer a break.
The best solution I have come up with is to leverage the apparent relationship between the country name and the enumeration key to forge a link between the two. I am sure others could improve on this solution but what I would really like to see is a solution that isn't a hack like this that relies on an apparent pattern but rather on that is based on an explicit connection. Or better yet NetSuite should just provide the data in one place all together.
For example you can see the apparent relationship here:
_alandIslands -> Aland Islands
With a little code I can try to forge a match.
I first get the Enumeration Keys into an array. And I create a list of objects of type NetSuiteCountry that will hold my results.
var countryEnumKeys = Enum.GetNames(typeof(Country));
var countries = new List<NetSuiteCountry>();
I then loop through the list of country Name and Code I got using the referenced code above (not shown here).
For each country name I then strip all non-word characters from the country name with Regex.Replace, prepend an underscore (_) and then convert the string to lowercase. Finally I try to find a match between the Enumeration Key (converted to lowercase as well) and the matcher string that was created. If a match is found I save all the data together the countries list.
UPDATE: Based on the comments I have added additional code/hacks to try to deal with the anomalies without hard-coding exceptions. Hopefully these updates will catch any future updates to the country list as well, but no promises. As of this writing it was able to handle all the known anomalies. In my case I needed to ignore Deprecated countries so those aren't included.
foreach (RecordRef baseRef in baseRefList)
{
var name = baseRef.name;
//Skip Deprecated countries
if (name.EndsWith("(Deprecated)")) continue;
//Use the name to try to find and enumkey match and only add a country if found.
var enumMatcher = $"_{Regex.Replace(name, #"\W", "").ToLower()}";
//Compares Ignoring Case and Diacritic characters
var enumMatch = CountryEnumKeys.FirstOrDefault(e => string.Compare(e, enumMatcher, CultureInfo.CurrentCulture, CompareOptions.IgnoreNonSpace | CompareOptions.IgnoreCase) == 0);
//Then try by Enum starts with Name but only one.
if (enumMatch == null)
{
var matches = CountryEnumKeys.Where(e => e.ToLower().StartsWith(enumMatcher));
if (matches.Count() == 1)
{
Debug.Write($"- Country Match Hack 1 : ");
enumMatch = matches.First();
}
}
//Then try by Name starts with Enum but only one.
if (enumMatch == null)
{
var matches = CountryEnumKeys.Where(e => enumMatcher.StartsWith(e.ToLower()));
if (matches.Count() == 1)
{
Debug.Write($"- Country Match Hack 2 : ");
enumMatch = matches.First();
}
}
//Finally try by first half Enum and Name match but again only one.
if (enumMatch == null)
{
var matches = CountryEnumKeys.Where(e => e.ToLower().StartsWith(enumMatcher.Substring(0, (enumMatcher.Length/2))));
if (matches.Count() == 1)
{
Debug.Write($"- Country Match Hack 3 : ");
enumMatch = matches.First();
}
}
if (enumMatch != null)
{
var enumIndex = Array.IndexOf(CountryEnumKeys, enumMatch);
if (enumIndex >= 0)
{
var country = (Country) enumIndex;
var nsCountry = new NetSuiteCountry
{
Name = baseRef.name,
Code = baseRef.internalId,
EnumKey = country.ToString(),
Country = country
};
Debug.WriteLine($"[{nsCountry.Name}] as [{nsCountry.EnumKey}]");
countries.Add(nsCountry);
}
}
else
{
Debug.WriteLine($"Could not find Country match for: [{name}] as [{enumMatcher}]");
}
}
Here is my NetSuiteCountry class:
public class NetSuiteCountry
{
public string Name { get; set; }
public string Code { get; set; }
public string EnumKey { get; set; }
public Country Country { get; set; }
}
Let me start off with a disclaimer that I'm not a coder, and this is the first day I've tried to look at a C# program.
I need something similar for a Javascript project where I need the complete list of Netsuite company names, codes and their numeric values and when reading the help it seemed like the only way was through webservices.
I downloaded the sample application for webservices from Netsuite and a version of Visual Studio and I was able to edit the sample program provided to create a list of all of the country names and country codes (ex. Canada, CA).
I started out doing something similar to the previous poster to get the list of country names:
string[] countryList = Enum.GetNames(typeof(Country));
foreach (string s in countryList)
{
_out.writeLn(s);
}
But I later got rid of this and started a new technique. I created a class similar to the previous answer:
public class NS_Country
{
public string countryCode { get; set; }
public string countryName { get; set; }
public string countryEnum { get; set; }
public string countryNumericID { get; set; }
}
Here is the new code for getting the list of company names, codes and IDs. I realize that it's not very efficient as I mentioned before I'm not really a coder and this is my first attempt with C#, lots of Google and cutting/pasting ;D.
_out.writeLn(" Attempting to get Country list.");
// Create a list for the NS_Country objects
List<NS_Country> CountryList = new List<NS_Country>();
// Create a new GetSelectValueFieldDescription object to use in a getSelectValue search
GetSelectValueFieldDescription countryDesc = new GetSelectValueFieldDescription();
countryDesc.recordType = RecordType.customer;
countryDesc.recordTypeSpecified = true;
countryDesc.sublist = "addressbooklist";
countryDesc.field = "country";
// Create a GetSelectValueResult object to hold the results of the search
GetSelectValueResult myResult = _service.getSelectValue(countryDesc, 0);
BaseRef[] baseRef = myResult.baseRefList;
foreach (BaseRef nsCountryRef in baseRef)
{
// Didn't know how to do this more efficiently
// Get the type for the BaseRef object, get the property for "internalId",
// then finally get it's value as string and assign it to myCountryCode
string myCountryCode = nsCountryRef.GetType().GetProperty("internalId").GetValue(nsCountryRef).ToString();
// Create a new NS_Country object
NS_Country countryToAdd = new NS_Country
{
countryCode = myCountryCode,
countryName = nsCountryRef.name,
// Call to a function to get the enum value based on the name
countryEnum = getCountryEnum(nsCountryRef.name)
};
try
{
// If the country enum was verified in the Countries enum
if (!String.IsNullOrEmpty(countryToAdd.countryEnum))
{
int countryEnumIndex = (int)Enum.Parse(typeof(Country), countryToAdd.countryEnum);
Debug.WriteLine("Enum: " + countryToAdd.countryEnum + ", Enum Index: " + countryEnumIndex);
_out.writeLn("ID: " + countryToAdd.countryCode + ", Name: " + countryToAdd.countryName + ", Enum: " + countryToAdd.countryEnum);
}
}
// There was a problem locating the country enum that was not handled
catch (Exception ex)
{
Debug.WriteLine("Enum: " + countryToAdd.countryEnum + ", Enum Index Not Found");
_out.writeLn("ID: " + countryToAdd.countryCode + ", Name: " + countryToAdd.countryName + ", Enum: Not Found");
}
// Add the countryToAdd object to the CountryList
CountryList.Add(countryToAdd);
}
// Create a JSON - I need this for my javascript
var javaScriptSerializer = new System.Web.Script.Serialization.JavaScriptSerializer();
string jsonString = javaScriptSerializer.Serialize(CountryList);
Debug.WriteLine(jsonString);
In order to get the enum values, I created a function called getCountryEnum:
static string getCountryEnum(string countryName)
{
// Create a dictionary for looking up the exceptions that can't be converted
// Don't know what Netsuite was thinking with these ones ;D
Dictionary<string, string> dictExceptions = new Dictionary<string, string>()
{
{"Congo, Democratic Republic of", "_congoDemocraticPeoplesRepublic"},
{"Myanmar (Burma)", "_myanmar"},
{"Wallis and Futuna", "_wallisAndFutunaIslands"}
};
// Replace with "'s" in the Country names with "s"
string countryName2 = Regex.Replace(countryName, #"\'s", "s");
// Call a function that replaces accented characters with non-accented equivalent
countryName2 = RemoveDiacritics(countryName2);
countryName2 = Regex.Replace(countryName2, #"\W", " ");
string[] separators = {" ","'"}; // "'" required to deal with country names like "Cote d'Ivoire"
string[] words = countryName2.Split(separators, StringSplitOptions.RemoveEmptyEntries);
for (var i = 0; i < words.Length; i++)
{
string word = words[i];
if (i == 0)
{
words[i] = char.ToLower(word[0]) + word.Substring(1);
}
else
{
words[i] = char.ToUpper(word[0]) + word.Substring(1);
}
}
string countryEnum2 = "_" + String.Join("", words);
// return an empty string if the country name contains Deprecated
bool b = countryName.Contains("Deprecated");
if (b)
{
return String.Empty;
}
else
{
// test to see if the country name was one of the exceptions
string test;
bool isExceptionCountry = dictExceptions.TryGetValue(countryName, out test);
if (isExceptionCountry == true)
{
return dictExceptions[countryName];
}
else
{
return countryEnum2;
}
}
}
In the above I used a function, RemoveDiacritics I found here. I will repost the referenced function below:
static string RemoveDiacritics(string text)
{
string formD = text.Normalize(NormalizationForm.FormD);
StringBuilder sb = new StringBuilder();
foreach (char ch in formD)
{
UnicodeCategory uc = CharUnicodeInfo.GetUnicodeCategory(ch);
if (uc != UnicodeCategory.NonSpacingMark)
{
sb.Append(ch);
}
}
return sb.ToString().Normalize(NormalizationForm.FormC);
}
Here are the tricky cases to test any solution you develop with:
// Test tricky names
Debug.WriteLine(getCountryEnum("Curaçao"));
Debug.WriteLine(getCountryEnum("Saint Barthélemy"));
Debug.WriteLine(getCountryEnum("Croatia/Hrvatska"));
Debug.WriteLine(getCountryEnum("Korea, Democratic People's Republic"));
Debug.WriteLine(getCountryEnum("US Minor Outlying Islands"));
Debug.WriteLine(getCountryEnum("Cote d'Ivoire"));
Debug.WriteLine(getCountryEnum("Heard and McDonald Islands"));
// Enums that fail
Debug.WriteLine(getCountryEnum("Congo, Democratic Republic of")); // _congoDemocraticPeoplesRepublic added to exceptions
Debug.WriteLine(getCountryEnum("Myanmar (Burma)")); // _myanmar added to exceptions
Debug.WriteLine(getCountryEnum("Netherlands Antilles (Deprecated)")); // Skip Deprecated
Debug.WriteLine(getCountryEnum("Serbia and Montenegro (Deprecated)")); // Skip Deprecated
Debug.WriteLine(getCountryEnum("Wallis and Futuna")); // _wallisAndFutunaIslands added to exceptions
For my purposes I wanted a JSON object that had all the values for Coutries (Name, Code, Enum, Value). I'll include it here in case anyone is searching for it. The numeric values are useful when you have a 3rd party HTML form that has to forward the information to a Netsuite online form.
Here is a link to the JSON object on Pastebin.
My appologies for the lack of programming knowledge (only really do a bit of javascript), hopefully this additional information will be useful for someone.
I have an array of valid e-mail address domains. Given an e-mail address, I want to see if its domain is valid
string[] validDomains = { "#test1.com", "#test2.com", "#test3.com" };
string email = "test#test1.com"
Is there a way to check if email contains any of the values of validDomains without using a loop?
I would like to recommend you the following code:
HashSet<string> validDomains = new HashSet<string>
{
"test1.com", "test2.com", "test3.com"
};
const string email = "test#test1.com";
MailAddress mailAddress = new MailAddress(email);
if (validDomains.Contains(mailAddress.Host))
{
// Contains!
}
HashSet.Contains Method is an O(1) operation; while array - O(n). So HashSet<T>.Contains is extremely fast. Also, HashSet does not store the duplicate values and there is no point to store them in your case.
MailAddress Class represents the address of an electronic mail sender or recipient. It contains mail address parsing logic (just not to reinvent the wheel).
If you want to be efficient, not only should you avoid using a loop, but you should construct a HashSet for your allowed domains, which would allow O(1) lookup:
string[] validDomains = { "#test1.com", "#test2.com", "#test3.com" };
HashSet<string> validDomainsHashSet = new HashSet<string>(validDomains);
string email = "test#test1.com";
string domain = email.Substring(email.IndexOf('#'));
bool isValidDomain = validDomainsHashSet.Contains(domain);
It would also make sense to exclude the # character from your domains, since it would be present in all and thereby redundant:
string[] validDomains = { "test1.com", "test2.com", "test3.com" };
HashSet<string> validDomainsHashSet = new HashSet<string>(validDomains);
string email = "test#test1.com";
string domain = email.Substring(email.IndexOf('#') + 1);
bool isValidDomain = validDomainsHashSet.Contains(domain);
The simplest way with LINQ (this also ignores the case):
bool validEmail = validDomains
.Any(d => email.EndsWith(d, StringComparer.OrdinalIgnoreCase));
int index = email.IndexOf("#");
var domain = email.Substring(index)
return validDomains.Any(x=>x == domain);
Check this out:
string[] validDomains = { "#test1.com", "#test2.com", "#test3.com" };
string email = "test#test1.com";
if (validDomains.Contains(email.Substring(email.IndexOf("#"))))
{
}
With a for each loop in this way :
string[] validDomains = { "#test1.com", "#test2.com", "#test3.com" };
string email = "test#test1.com";
foreach (string x in validDomains)
{
if (email.Contains(x))
{
// Do Something
}
}
Without a loop in this way(with LINQ) :
if(validDomains.Any(s => email.Contains(s))) {
//Do Something
}
validDomains.Any(validDomain => email.EndsWith(validDomain))
Refer to the documentation of IEnumerable.Any for more details.
I have a simple class like this:
class QuickReport
{
public string DeviceName { get; set; }
public string GroupName { get; set; }
public string PinName { get; set; }
public override string ToString()
{
return DeviceName + "," + GroupName + "," + PinName;
}
}
Later I make a list of items with this class:
List<QuickReport> QR = new List<QuickReport>();
Later in my program it will fill up and when I save it in a text file it will be like this example:
HBM\D1,GND,10
HBM\D1,GND,12
HBM\D1,NT_IOp,115
HBM\D1,NT_IOp,117
HBM\D2,GND,8
HBM\D2,NT_IOp,115
HBM\D2,NT_IOp,116
Now I want to make a function to save the text file in more readable manner. That is formatting it by DEVICE, GROUPS and PINS. So the above example would result in:
HBM\D1
GND: 10, 12
NT_IOp: 115, 117
HBM\D2
GND: 8
NT_IOp: 115, 116
can you please help and give some ideas?
Thanks!
var query = QR.ToLookup(i=>i.DeviceName, i => new {i.GroupName, i.PinName})
.Select(i=>
new {DeviceName = i.Key,
Groups = i.ToLookup(g=>g.GroupName, g=>g.PinName)});
var sb = new StringBuilder();
foreach ( var device in query)
{
sb.AppendLine(device.DeviceName);
foreach ( var gr in device.Groups)
{
sb.Append(gr.Key + ": ");
sb.Append(String.Join(", ", gr.ToArray()));
sb.AppendLine();
}
sb.AppendLine();
}
var stringToWrite = sb.ToString();
As i understand you have tree structure, where Device have child Groups, and Groups have child pins.
You can create custom classes like this:
class Group
{
string Name;
//pins that belong to this group
List<string> pins;
}
class Device
{
string Name;
//groups that belong to this device
List<Group> Groups;
}
And than just collect it to List<Device> and serialize it using XML Serialization.
This isn't complete, but it should give you enough to go on. You'll still need to add your newlines, and remove trailing commas, etc.
// Make your key the device name
var qrHash = new Dictionary<string, List<QuickReport>>();
// Populate your QR Dictionary here.
var output = new StringBuilder();
foreach (var keyValuePair in qrHash)
{
output.Append(keyValuePair.Key);
var gnd = new StringBuilder("GND: ");
var nt = new StringBuilder("NT_IOp: ");
foreach (var qr in keyValuePair.Value)
{
gnd.Append(qr.GroupName);
nt.Append(qr.PinName);
}
output.Append(gnd);
output.Append(nt);
}
How about using the XmlSerializer to serialize and deserialize your class? This should provide some readable output.
http://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlserializer.aspx
The quickest ways I can think of to do this would either be to loop over the List<> 3 times, eachtime checking on a seperate accessor, writing it out to a StringBuilder, then returning StringBuilder.ToString() from the function.
Or, you could use 3 stringbuilders to hold each accessor type, then push all 3 from the function on return.