Combine 4 parameters into something that only three arguments? - c#

I've got some static data that I'm experimenting with in C# and the first method looks like this which is basically declaring them.
public class PrivilegeProfiles
{
string PROFILE_ID;
string COMPANY_CODE;
string PRIVILEGE_CODE;
public PrivilegeProfiles(string PROFILE_ID, string COMPANY_CODE, string PRIVILEGE_CODE)
{
this.PROFILE_ID = PROFILE_ID;
this.COMPANY_CODE = COMPANY_CODE;
this.PRIVILEGE_CODE = PRIVILEGE_CODE;
}
}
that's all fine and good but I've got a second method with a .Add keyword and since it only takes 3 arguments I can't add all the static data I need. PRIVILEGE_CODE has multiple bits of data where as PROFILE_ID and COMPANY_CODE only have one. Are there certain brackets I've gotta use or is there a way I've gotta format it for it to work?
public ServiceResponse GetPrivileges()
{
ServiceResponse sR = new ServiceResponse();
List<PrivilegeProfiles> privilegeProfiles;
privilegeProfiles.Add(new PrivilegeProfiles("Train Manager","GW",["DASuper" "DAAccess" "MRSuper", "MRAccess"]);
sR.DataResponse=privilegeProfiles;
return sR;
}

I think you might want the PRIVILEGE_CODE field to be an array of strings instead of a string. For example:
public class PrivilegeProfiles
{
string PROFILE_ID;
string COMPANY_CODE;
string[] PRIVILEGE_CODE;
public PrivilegeProfiles(string aPROFILE_ID, string aCOMPANY_CODE, string[] aPRIVILEGE_CODE)
{
this.PROFILE_ID = aPROFILE_ID;
this.COMPANY_CODE = aCOMPANY_CODE;
this.PRIVILEGE_CODE = aPRIVILEGE_CODE;
}
}
and
public ServiceResponse GetPrivileges()
{
ServiceResponse sR = new ServiceResponse();
List<PrivilegeProfiles> privilegeProfiles;
privilegeProfiles.Add(new PrivilegeProfiles("Train Manager","GW", new string[] {"DASuper","DAAccess","MRSuper","MRAccess"});
sR.DataResponse=privilegeProfiles;
return sR;
}

You either add more variables to your PrivilegeProfiles class that can hold all the information you have or you find a format so that all your PRIVILEGE_CODE data fits into a string. Some examples for your ["DASuper" "DAAccess" "MRSuper", "MRAccess"] as a string could be:
"DASuper,DAAccess,MRSuper,MRAccess"
"DASuper;DAAccess;MRSuper;MRAccess"
"DASuper DAAccess MRSuper MRAccess"
whatever you please

Related

Strongly typed parsing of CSV-files

So after about an hour's worth of pulling my hair in desperation, I decided to follow the advice from, like, everybody in here and not implement my own CSV-parser.
So I went with FileHelpers instead.
But I am having a bit of trouble using it correctly.
My CSV-file looks something like this:
50382018,50319368,eBusiness Manager,IT02,3350_FIB4,IT,2480
50370383,50373053,CRM Manager,IT01,3200_FIB3,xyz,2480
50320067,50341107,"VP, Business Information Officer",IT03,3200_FI89,xyz,2480
50299061,50350088,Project Expert,IT02,8118_FI09,abc,2480
My need for FileHelpers (and, specifically CsvEngine) is in line 3 - notice third column enclosed in quotes since it has an internal comma (which is otherwise used as delimiter).
My code to read the file is this:
var co = new FileHelpers.Options.CsvOptions("Employee", columnDeliminator, 7);
var ce = new CsvEngine(co);
var records = ce.ReadFile(pathToCSVFile);
It works fine - sort of. It correctly parses the lines and recognizes the values with enclosed delimiters.
But.
The return value of the ReadFile()-method is object[]. And the contents of it appears to be some kind of dynamic type.
It looks something like this - where the columns are named "Field_1", "Field_2" etc.
I have created a "data class" intended to hold the parsed lines It looks like this:
public class Employee
{
public string DepartmentPosition;
public string ParentDepartmentPosition;
public string JobTitle;
public string Role;
public string Location;
public string NameLocation;
public string EmployeeStatus;
}
Is there a way to have FileHelpers' CsvEngine class to return strongly typed data?
If I could just use the "basic" parser of FileHelpers, I could use this code:
var engine = new FileHelperEngine<Employee>();
var records = engine.ReadFile("Input.txt");
Is there a way to have CsvEngine return instances of my "Employee" class? Or do I have to write my own mapping code to support this?
#shamp00 has the correct answer - and I also found it at FileHelper escape delimiter .
I took my model class and decorated each property on it as suggested:
(I probably don't need to decorate all properties, but it works for now)
[DelimitedRecord((","))]
public class Employee
{
[FieldQuoted('"', QuoteMode.OptionalForBoth)]
public string DepartmentPosition;
[FieldQuoted('"', QuoteMode.OptionalForBoth)]
public string ParentDepartmentPosition;
[FieldQuoted('"', QuoteMode.OptionalForBoth)]
public string JobTitle;
[FieldQuoted('"', QuoteMode.OptionalForBoth)]
public string Role;
[FieldQuoted('"', QuoteMode.OptionalForBoth)]
public string Location;
[FieldQuoted('"', QuoteMode.OptionalForBoth)]
public string NameLocation;
[FieldQuoted('"', QuoteMode.OptionalForBoth)]
public string EmployeeStatus;
}
Now I just need this code:
TextReader reader = new StreamReader(contents);
var engine = new FileHelperEngine<Employee>()
{
Options = { IgnoreFirstLines = 1 }
};
var myRecords = engine.ReadStream(reader);
The documentation worked for me for a one simple way:
First in your class, it needs a couple decorators:
Edit Use the FieldQuoted decorator to parse anything in quotes and ignore the included comma
[DelimitedRecord(",")]
class Person
{
[FieldQuoted]
public string Name { get; set; }
[FieldConverter(ConverterKind.Int32)]
public int Age { get; set; }
public string State { get; set; }
}
DelimitedRecord for the class and the expected delimiter (this could be a problem if things change later.
and FieldConverter for it appears anything other than string.
Then change your reading method slightly:
var fhr = new FileHelperEngine<Person>();
var readLines = fhr.ReadFile(pathToFile);
and then it works, strongly typed:
foreach(var person in readLines)
{
Console.WriteLine(person.Name);
}
Using CsvHelper as a viable alternative and assuming the CSV file has no headers,
a mapping can be created for the Employee class like
public sealed class EmployeeClassMap : ClassMap<Employee> {
public EmployeeClassMap() {
Map(_ => _.Location).Index(0);
Map(_ => _.NameLocation).Index(1);
Map(_ => _.JobTitle).Index(2);
//...removed for brevity
}
}
Where the index is mapped to a respective property on the strongly typed object model.
To use this mapping, you need to register the mapping in the configuration.
using (var textReader = new StreamReader(pathToCSVFile)) {
var csv = new CsvReader(textReader);
csv.Configuration.RegisterClassMap<EmployeeClassMap>();
var records = csv.GetRecords<Employee>();
//...
}
If this lib not work, you can also try to use built-in .Net CSV parser TextFieldParser. For ex: https://coding.abel.nu/2012/06/built-in-net-csv-parser/
ADDED:
For types (with auto convert):
static void run()
{
// split with any lib line of CSV
string[] line = new string[]{"john", "doe", "201"};
// needed prop names of class
string[] propNames = "fname|lname|room".Split('|');
Person p = new Person();
parseLine<Person>(p, line, propNames);
}
static void parseLine<T>(T t, string[] line, string[] propNames)
{
for(int i = 0;i<propNames.Length;i++)
{
string sprop = propNames[i];
PropertyInfo prop = t.GetType().GetProperty(sprop);
object val = Convert.ChangeType(line[i], prop.PropertyType);
prop.SetValue(t, val );
}
}
class Person
{
public string fname{get;set;}
public string lname{get;set;}
public int room {get;set;}
}

return and call several arrays

I have got a method which returns five arrays of different dimensions:
public static (string[],string[],string[,],string[,],string[,]) deserializeobject(string filename)
{
return (Auftraggeber, Aufstellungsort, Anlagen, Vorgang_rückgemeldet, Vorgang_zukünftig);
}
How do I correctly call this method to further work with the arrays?
I would stronly suggest to create a class for that, in particular as the method is public and thus could be used in multiple contexts. That makes it far easier for users of your API to determine the meaning of every returned member.
Your individual members seem to have a descene tmeaning anyway, so why throw it away and return a collection of namesless paramaters?
class MyObject
{
public string[] Auftraggeber { get; set; }
public string[] Aufstellungsort { get; set; }
public string[] Anlagen { get; set; }
public string[] Vorgang_rückgemeldet { get; set; }
public string[] Vorgang_zukünftig { get; set; }
}
And:
public static MyObject Deserialize(string fileName)
{
return new MyObject { AuftragGeber = ... };
}
Now users of your method can easily determine what the parameters mean:
var o = deserialize(myFile);
DoSomething(o.Aufstellungsort);
which is far easier to read than this:
var o DoSomething(myFile);
DoSomething(o.Item2);
isn´t it? Apart from this it limits typos. In the second case users can easily type Item2 when they actually ment Item1, which may cause headache when debugging. With a descent name for every member those typos are far more unlikely.
First the response to your question:
Given:
public static (string[], string[], string[,], string[,], string[,]) deserializeobject(string filename)
{
// Some value that will be returned... Just doing a test here
return default((string[], string[], string[,], string[,], string[,]));
}
You can:
// Using var
var des1 = deserializeobject("foo.bin");
Console.WriteLine($"Lengths: {des1.Item1.Length}, {des1.Item2.Length}, {des1.Item3.Length}, {des1.Item4.Length}, {des1.Item5.Length}");
// Legal, but too much verbose
(string[], string[], string[,], string[,], string[,]) des2 = deserializeobject("foo.bin");
// Legal too, because in truth the ValueTuple<> struct is used
ValueTuple<string[], string[], string[,], string[,], string[,]> des3 = deserializeobject("foo.bin");
Now, the problem here is that, as I've written in a comment, you need a special type of hate for your coworkers to do this. Why? Because if I ask you, what is Item4, can you give me a response? No :-) Fortunately there are two alternatives: creating a full class/struct to contain the return value or using named tuples. I'm quite against creating a class that will be used only by a single method, so I'll show you the named tuples way.
Using named tuples you can:
public static (string[] Auftraggeber, string[] Aufstellungsort, string[,] Anlagen, string[,] VorgangRückgemeldet, string[,] VorgangZukünftig) deserializeobject2(string filename)
{
return default((string[], string[], string[,], string[,], string[,]));
}
Then you can:
// Using var, with named arguments:
var des4 = deserializeobject2("foo.bin");
Console.WriteLine($"Lengths: {des4.Auftraggeber.Length}, {des4.Aufstellungsort.Length}, {des4.Anlagen.Length}, {des4.VorgangRückgemeldet.Length}, {des4.VorgangZukünftig.Length}");
See? The name of the items (arrays) returned by your method is maintained...
Note that named tuples are a sleight of hand. There are no named tuples underside. There are only ValueTuple<> that are "annotated" with the name of the properties that you want.
This is legal:
ValueTuple<string[], string[], string[,], string[,], string[,]> des5 = des4;
Full example to the question in comment:
public static (string[] Auftraggeber, string[] Aufstellungsort, string[,] Anlagen, string[,] VorgangRückgemeldet, string[,] VorgangZukünftig) deserializeobject2(string filename)
{
// Define/create/fill the arrays
var auftraggeber = new string[10];
var aufstellungsort = new string[10];
var anlagen = new string[10, 10];
var vorgangRückgemeldet = new string[10, 10];
var vorgangZukünftig = new string[10, 10];
// Return the arrays
return (auftraggeber, aufstellungsort, anlagen, vorgangRückgemeldet, vorgangZukünftig);
}
use Tuple<> like below code :
public Tuple<int[], int[]> GetMultipleValue(string name)
{
int[] a = new int[]{1,2,3,4}
int[] b = new int[]{5,6,7,8}
return Tuple.Create(a,b);
}

C# string format unknown size

Let's say I have the following string:
string postData = "state={2}&country={3}&geolocation={0}&city={1}";
And I have another list of strings. Its size could be in this case 4 at most.
I'm trying to create a method which replaces the numbers in my postData variable, depending on the list size. Something like the following method:
private string UnknownSizeStringFormat(string postData, params string[] stringsToReplace)
{
return string.format(postData, stringsToReplace);
}
The above method works as long as the list's size is 4. The thing is, on the first call, my list's size could be smaller than 4, so if it's 0 for example, I would like to replace each number inside the brackets with an empty string. My return value should be:
"state=&country=&geolocation=&city="
If its size is one, and the first member in the list is "21,27" my return string should be:
"state=&country=&geolocation=21,27&city="
And so on...
I could use a loop or a Regular Expression for this purpose but I've been wondering if there is a better way, a Linq solution perhaps? What I do know is how many numbers postData could have at most, which in my case is 4.
Again, I could do that with a loop or a Regular Expression, but I'm trying to make it as short as possible
Edit: The postData string could vary. That was just an example. Its size or content could be different
What I do know is how many numbers postData could have at most
Then how about this:
static string UnknownSizeStringFormat(string format, int maxArgs, params object[] args)
{
Array.Resize(ref args, maxArgs);
return string.Format(format, args);
}
so you can use:
string postData = "state={2}&country={3}&geolocation={0}&city={1}";
var result = UnknownSizeStringFormat(postData, 4);
You could use something like this:
private string UnknownSizeStringFormat(string postData, params string[] stringsToReplace)
{
string[] temp = { "", "", "", "" };
Array.ConstrainedCopy(stringsToReplace, 0, temp, 0, stringsToReplace.Length);
return string.format(postData, temp);
}
If you are allowed to write in C#6, then I would suggest you the following quick solution
Define a class that has as properties the parameters you refer to and ovveride the ToString method in a such a way to return the url you want.
public class CustomUrl
{
public string State {get;set;}
public string Country { get;set;}
public string Geolocation {get;set;}
public string City {get;set;}
public ovveride string ToString() =>
$"state={State}&country={Country}&geolocation={Geolocation}&city={City}";
}
you can use it as:
var customUrl = new CustomUrl
{
Geolocation = "21,27";
}
Then calling customUrl.ToString(), you would get:
"state=&country=&geolocation=21,27&city="
While creating another customer url as:
var customUrl = new CustomUrl();
and the calling customUrl.ToString() you would get:
"state=&country=&geolocation=&city="
If you are not allowed to write in C#, you have to modify a bit the class's definition, like below:
public class CustomUrl
{
public string State {get;set;}
public string Country { get;set;}
public string Geolocation {get;set;}
public string City {get;set;}
public ovveride string ToString()
{
retrun string.Format("state={0}&country={1}&geolocation={2}&city={3}",State,Country,Geolocation,City);
}
}
However, the best solution can be found at Named Formats Redux, the Henri formatter. If you implement this, you could call it as an extension method, like below:
var template = "state={state}&country={country}&geolocation={geo}&city={city}";
var customUrl = template.HenriFormat(new { geo = "21,72"});
The reason I say that this is the best solution is the fact that you would implement it once and you could use it anywhere, without have to implement a custom class for a case like the above.
I would start by adjusting your postData based on stringsToReplace.Length. You can use a switch/case method to control this. This is non-tested code, so please use with debug to verify.
string postData = "state={2}&country={3}&geolocation={0}&city={1}";
private string UnknownSizeStringFormat(string postData, params string[] stringsToReplace)
{
switch(stringsToReplace.Length){
case 0:
postData = "state={0}&country={0}&geolocation={0}&city={0}";
break;
case 1:
postData = "state={0}&country={0}&geolocation={1}&city={0}";
break;
case 2:
postData = "state={2}&country={0}&geolocation={1}&city={0}";
break;
case 3:
postData = "state={2}&country={3}&geolocation={1}&city={0}";
break;
case 4:
postData = "state={2}&country={3}&geolocation={1}&city={4}";
break;
return string.format(postData, String.Empty, stringsToReplace);
}
}

Issues with List.Add method when using a variable in C#

I currently am unable to use a variable that is a string to Add as an item to a list. It simply returns null when I pull the list later:
public class JobStatus
{
public static string _JobURI;
public static string currentStatus = "no job";
public static void checkStatus()
{
...
//define job URI
List<string> jobURIs = new List<string>();
jobURIs.Add(_JobURI);
However, when I insert a string value like below instead of a variable, it adds it properly to the list:
//define job URI
List<string> jobURIs = new List<string>();
jobURIs.Add("new item name");
I'm not sure what I'm missing.
Based on your posted code, the reason you are getting null for _JobsURI is that you declare it here:
public static string _JobURI;
But you never assign it a value. Per the documentation: "A string that has been declared but has not been assigned a value is null."
Try assigning a value to _JobURI and then adding it to the List<string>:
public static string _JobURI = "Some string here.";
I figured it out, beginning programmer mistake. I used the Get Set method in the same class and declared the variable as well you all suggested above:
public static string currentStatus = "no job";
private static string joburi = "";
public static string JobURI
{
get { return joburi; }
set { joburi = value; }
}
Thank you for your help.

C# call method on a member class

I want do something like that:
public class MyClass
{
public String varA{ get; set; }
public String[] varB{ get; set; }
//.....
public ?? string ToHtml()
{
//return HTML value
}
}
public class Run()
{
MyClass c = new Myclass();
c.varA = "Toto";
c.varB = new string[] { "foo", "bar" };
string a = c.varA.ToHtml() // -> "<p>Toto</p>";
string b = c.varB.ToHtml() // -> "<ul><li>foo</li><li>bar</li></ul>";
}
How can do that ?
Edit: I have change the Run()
Create an extension method on the String class, which returns a HTML representation of that string (or whatever ToHtml() should do).
public static class StringExtensions
{
public static string ToHtml ( this string target )
{
// TODO :
}
}
This is a way to implement your scenario with extension methods. While, as others have noted, it would make sense to keep the logic to turn your strings to HTML within MyClass, in certain scenarios it might make sense to use this approach.
public class MyClass
{
public String varA{ get; set; }
public String[] varB{ get; set; }
}
public static class MyExtensions {
public static string ToHtml(this string input, string format)
{
return string.Format(format, input);
}
public static string ToHtml(this string input)
{
return ToHtml(input,"<p>{0}</p>");
}
public static string ToHtml(this IEnumerable<string> input, string wrapperformat, string elementformat)
{
string body= input
.Select(s => string.Format(elementformat,s))
.Aggregate((a,b)=> a+b);
return string.Format(wrapperformat,body);
}
public static string ToHtml(this IEnumerable<string> input)
{
return ToHtml(input,"<ul>{0}</ul>","<li>{0}</li>");
}
}
Unless you define ToHtml() extension methods for both strings and arrays, you can't call it that way (on the fields themselves). See the other answers for how to implement extension methods.
A simpler alternative that applies to just MyClass is to make two overloads of your method which accept a string and a string array as arguments respectively, then pass them:
public string ToHtml(string arg)
{
//return HTML value
}
public string ToHtml(string[] argAsArray)
{
//return HTML value
}
Then call them like this:
string a = c.ToHtml(c.varA);
string b = c.ToHtml(c.varB);
What you're trying to do is add an helper method to the string class. It's called an extension method and it must respect certains rules :
It must use this on the first parameter
It must be static
It must be in a static class
.
public static class HtmlStringHelper
{
public static string ToHtml(this string s)
{
// Add you logic here
return (s);
}
}
i suppose you could use an extension method (for both string and string[] for that), but then you would not need the ToHtml() method of MyClass (as the logic would reside in the extension methods).
edit: i should note that calling member methods usually is considered a bad practice. Without more information its hard to imagine what MyClass is supposed to do/be, but you might want to keep control of class members in the class. So another way would be to create a ToHtmlA() and ToHtmlB() method for MyClass.
If you changed ToHtml to except a value:
public static string ToHtml(string a)
{
// your code here - obviously not returning ""
return "";
}
then you can call it like so:
MyClass c = new MyClass();
c.varA = "some text";
c.varA = MyClass.ToHtml(c.varA);
But, I maybe WAY off what you require.

Categories