Object to comma separated string - c#

is there a way to make a comma separated from an object. Note its object not List of object
Ex:
public class EmployeeLogReportListViewModel
{
public DateTime Date { get; set; }
public int EmployeeID { get; set; }
public TimeSpan Time { get; set; }
public int Sort { get; set; }
public string Employer { get; set; }
}
With the following values
Date = "2018/02/03"
EmployeeID = 111
Time = 11:53 AM
Sort = 1
Employer = EMP
this should result into
2018/02/03,111,11:53 AM,1 EMP
What is the best way to make this. possible single line of code cause i dont want to use string builder and append all of it.

I think you are looking for Overridden .ToString() method. you have to modify the class like this:
public class EmployeeLogReportListViewModel
{
public DateTime Date { get; set; }
public int EmployeeID { get; set; }
public TimeSpan Time { get; set; }
public int Sort { get; set; }
public string Employer { get; set; }
public override string ToString()
{
return String.Format("{0},{1},{2},{3},{4}", this.Date, this.EmployeeID, this.Time, this.Sort, this.Employer);
}
}
Usage Example:
EmployeeLogReportListViewModel objVm = new EmployeeLogReportListViewModel();
// Assign values for the properties
objVm.ToString(); // This will give you the expected output

Challenge accepted
var o = new EmployeeLogReportListViewModel();
var text = string.Join
(
",",
typeof(EmployeeLogReportListViewModel)
.GetProperties(BindingFlags.Instance | BindingFlags.Public)
.Select
(
prop => prop.GetValue(o).ToString()
)
);
Console.WriteLine(text);
Technically that is one line.
If you want the properties sorted alphabetically, you could use this:
var o = new EmployeeLogReportListViewModel();
var text = string.Join
(
",",
typeof(EmployeeLogReportListViewModel)
.GetProperties(BindingFlags.Instance | BindingFlags.Public)
.OrderBy( prop => prop.Name )
.Select
(
prop => prop.GetValue(o).ToString()
)
);
Console.WriteLine(text);

It's a bit late to reply, but I can imagine you want to do that in order to have some kind of csv output style
a nice and generic way to do it is to create a extension method that transform any Enumerable into a csv string.
so borrowing #John Wu's solution, we come up with something like
public static class EnumerableToCsvExtension
{
public static string ToCSVString<TContent>(this IEnumerable<TContent> enumerable, char propertySeparator = ',', bool includeHeader = true)
{
var properties = typeof(TContent).GetProperties(BindingFlags.Instance | BindingFlags.Public);
var header = string.Join(propertySeparator, properties.Select(p => p.Name));
var rows = enumerable.ToList().ConvertAll(item => string.Join(propertySeparator, properties.Select(p => p.GetValue(item) ?? string.Empty )));
var csvArray = includeHeader ? rows.Prepend(header) : rows;
return string.Join(Environment.NewLine, csvArray);
}
}
then you use it like
var list = new List<EmployeeLogReportListViewModel> { new EmployeeLogReportListViewModel(), new EmployeeLogReportListViewModel() };
list.ToCSVString();
list.ToCSVString(propertySeparator: '|', includeHeader: false);

You can use like below:
public class bKashAccountInfo
{
public string bKashNumber { get; set; }
public string bKashAccountType { get; set; }
public override string ToString()
{
return String.Format($"bKash Number-{bKashNumber}, Account Type - {bKashAccountType}");
}
}

Related

Replace all the different Placeholder with Class properties value

I have a class as below
public class Details
{
public string CreatedAt {set;get;)
public Order Order { get; set; }
public Customer Customer { get; set; }
}
public class Customer
{
public string Name { get; set; }
public CustomerAddress Address { get; set; }
}
public class CustomerAddress
{
public string Line1 { get; set; }
public string Line2 { get; set; }
public string City { get; set; }
public string State { get; set; }
}
and I have HTML file with HTML content and few placeholder. I am replacing placeholders as below.
public static string ReplaceStringPlaceHolders(User Details)
{
string MyHTML= File.ReadAllText(#"...Path");
//Replacing one by one
string newstring= MyHTML.
.Replace("{created_at}", Details.CreatedAt)
.Replace("{customer_info.address.line_1}", Details.Customer.Address.Line1)
.Replace("{customer_info.address.line_2}", Details.Customer.Address.Line2)
.Replace("{customer_info.address.city}", Details.Customer.Address.City)
.Replace("{customer_info.address.state}", Details.Customer.Address.State)
.Replace("{customer_info.address.postal_code}", Details.Customer.Address.PostalCode)
.Replace("{customer_info.address.country}", Details.Customer.Address.Country)
return newstring;
}
but I don't like this way as I have put 50+ placeholders in my HTML file.
Is there a way that we can replace the placeholder when the placeholder name matches to class properties.
I am looking for something like this if possible:
MyHTML.replaceifPlaceHolderMatchesWithClassProperties(Label);
Kindly suggest.
Yes, you can read properties with a help of Reflection and Linq:
using System.Linq;
using System.Reflection;
....
private static string TryReadReflectionValue(object data, string name) {
if (name == null)
return null;
foreach (string item in name.Replace("_", "").Trim('{', '}').Split('.')) {
if (data == null)
return null;
var prop = data
.GetType()
.GetProperties(BindingFlags.Instance | BindingFlags.Static |
BindingFlags.Public)
.Where(p => p.Name.Equals(item, StringComparison.OrdinalIgnoreCase))
.Where(p => p.CanRead)
.Where(p => p.GetIndexParameters().Length <= 0)
.FirstOrDefault();
if (prop == null)
return null;
data = prop.GetValue(prop.GetGetMethod().IsStatic ? null : data);
}
return data?.ToString();
}
And match placeholders with a help of regular expressions:
using System.Text.RegularExpressions;
...
private static string MyReplace(string text, object data) {
if (text == null)
return text;
return Regex.Replace(
text,
#"\{\w._]+\}",
match => TryReadReflectionValue(data, match.Value) ?? match.Value);
}
Usage:
public static string ReplaceStringPlaceHolders(User Details) {
string MyHTML= File.ReadAllText(#"...Path");
return MyReplace(text, Details);
}
Here I assumed that
We always drop _ in placeholders, e.g {created_at} corresponds to CreatedAt property
We ignore case in placeholders: {created_at} == {Created_At} == {CREATED_at} etc.
All placeholders are in {letters,digits,_,.} format
i think this will help you:
string MyHTML = "{PersonId}, {Name}, {Family}, {Age}";
Person p = new Person();
p.PersonId = 0;
p.Name = "hello";
p.Family = "world";
p.Age = 15;
var properties = typeof(Person).GetProperties().ToDictionary(x => x, x => x.GetType().GetProperties());
foreach (var item in properties)
MyHTML = MyHTML.Replace("{" + item.Key.Name + "}", typeof(Person).GetProperty(item.Key.Name).GetValue(p).ToString());
Console.WriteLine(MyHTML);
output: 0, hello, world, 15

Cannot implicitly convert type IQueryable to class

I am trying to get multiple results into a list to send back to JS to populate a grid. The first query (_mappedQuery) is getting data. I then want to end up putting the values into the _udfList object. I keep getting variances on the error 'cannot convert queryable to class'
I have tried setting as lists, creating query objects, single class objects. All no luck so far
MemberMNCFormsList _udfList = new MemberMNCFormsList();
foreach (var _row in _udfTables) {
System.Diagnostics.Debugger.Break();
System.Diagnostics.Debugger.Launch();
var _mappedQuery = (from res in Query<UdfColumnMapping>().AsNoTracking()
join udf in Query<UserDefinedForms>().AsNoTracking() on res.Func_Area equals udf.Func_Area
join ds in Query<Data_Set>().AsNoTracking() on res.Data_ID equals ds.DATA_ID
join df in Query<DEFITEM>().AsNoTracking() on ds.DEF_ID equals df.DEF_ID
where udf.UserDefinedForms_ID == _row.UserDefinedForms_ID &&
(res.FieldName.ToLower().StartsWith("reviewname") ||
res.FieldName.ToLower().StartsWith("disposition") ||
res.FieldName.ToLower().StartsWith("reviewdate"))
select (new MemberMNCForms {
UserDefinedFormData_ID = _row.UserDefinedFormData_ID,
FormId = udf.UserDefinedForms_ID,
MappedColumnName = res.MappedColumnName,
FieldName = res.FieldName,
MappedTableName = res.MappedTableName,
Reviewed_Name = _row.LAST_NAME.Trim() + ", " + _row.FIRST_NAME.Trim(),
Reviewed_Date = _row.CreateDate.GetShortDateorEmpty().ToString()
}));
var _formRow = _mappedQuery.Select(t => new MemberMNCForms {
UserDefinedFormData_ID = t.UserDefinedFormData_ID,
FormId = t.FormId,
MappedColumnName = t.MappedColumnName,
FieldName = t.FieldName,
MappedTableName = t.MappedTableName,
Reviewed_Name = t.Reviewed_Name,
Reviewed_Date = t.Reviewed_Date
})));
_udfList.list.Add(_formRow);
public sealed class MemberMNCForms {
public Guid? UserDefinedFormData_ID { get; set; }
public int FormId { get; set; }
public string Reviewed_Name { get; set; }
public string MappedColumnName { get; set; }
public string FieldName { get; set; }
public string MappedTableName { get; set; }
public int? MNCDetermination_ID { get; set; }
public string Reviewed_By { get; set; }
public string Reviewed_Date { get; set; }
}
public sealed class MemberMNCFormsList : ErrorInfo
{
public List<MemberMNCForms> list = new List<MemberMNCForms>();
public int Count { get; set; }
}
I am trying to get the _udfList object populated with the values coming from _mappedQuery. The only thing I thought would work was to create a MemberMNCForms object for each record in _mappedQuery to then add to _udfList.list
_formRow is an IEnumerable<MemberMNCForms>
var _formRow = _mappedQuery.Select(t => new MemberMNCForms {
UserDefinedFormData_ID = t.UserDefinedFormData_ID,
FormId = t.FormId,
MappedColumnName = t.MappedColumnName,
FieldName = t.FieldName,
MappedTableName = t.MappedTableName,
Reviewed_Name = t.Reviewed_Name,
Reviewed_Date = t.Reviewed_Date
})));
Here you are trying to add an IEnumerable<MemberMNCForms> to a List<MemberMNCForms>
_udfList.list.Add(_formRow);
You can't do this with .Add. You have to use .AddRange
Try this:
_udfList.list.AddRange(_formRow);
When you use linq like that, even if there is a single item in the list that you are Selecting on, it is just an expression tree until it is iterated on.
I assume that you are expecting a collection of MemberMNCForms back so you would need use AddRange instead of Add
_udfList.list.AddRange(_formRow);
To make sure that it has been executed, you can use ToList
_udfList.list.AddRange(_formRow.ToList());
If you are just expecting a single result, you can use SingleOrDefault.
var result = _formRow.SingleOrDefault();
if (result != null) {
_udfList.list.Add(result);
}

Is there a way to deserialize CSV to object, mapping by varied column names?

I would like to deserialize CSVs to objects, mapping by varied column names, as illustrated by the following examples:
Input 1
Id;Name;Document
1;Matheus;555777
2;Clarice;567890
Input 2
"Id_Person";"First_Name";"Phone"
3;"John";"999-9999"
public class People
{
public int PersonId { get; set; }
public string Name { get; set; }
public string Doc { get; set; }
}
Note that the column names change by file, and a column can even be missing.
I would like to map "Id" and "Id_Person" to the PersonId property, and so on.
How to do it?
Actually found something that solved my problem: CsvHelper
Setting up:
public sealed class PessoaCSVMap : ClassMap<Pessoas>
{
public PessoaCSVMap()
{
Map(m => m.NomeCompleto).Name("Nome", "Name");
Map(m => m.Documento).Name("Documento", "Doc", "CPF");
Map(m => m.Email1).Name("Email", "Email1", "E-mail", "E-mail1");
Map(m => m.PessoaId).Ignore();
}
}
Using:
const string CSV = "Nome;Email;bleu\nMatheus;matheus.lacerda#email.com.br;blau\nClarice;null;null";
CsvReader csv = new CsvReader(new StringReader(CSV));
csv.Configuration.RegisterClassMap<PessoaCSVMap>();
csv.Configuration.Delimiter = ";";
csv.Configuration.HeaderValidated = null;
csv.Configuration.MissingFieldFound = null;
List<Pessoas> pessoas = csv.GetRecords<Pessoas>().ToList();
Assuming you already can read a specific CSV Line, this one manages to give a lot of flexibility with some reflection and generics . Not a works for all method, but it's easy to adapt it for different cases :
public IEnumerable<T> ProccessCSV<T>(StreamReader document, char divider)
where T : class, new()
{
string currentLine = reader.ReadLine();
string[] usedHeaders = currentLine.Split(divider);
while ((currentLine = reader.ReadLine()) != null)
{
var fields = currentLine.Split(divider);
var result = new T();
for (var i = 0; i < usedHeaders.Length; i++)
{
var value = fields[i];
var propInfo = typeof(T).GetProperties()[i];
result.GetType()
.GetProperty(propInfo.Name)
.SetValue(result, value);
}
yield return result;
}
}
Hope this works as a backbone to a possible solution.
For the first case, you'll need to use a separator ';' and a class like :
public class Person
{
public string Id { get; set; }
public string Name { get; set; }
public string Document { get; set; }
}
Based on : https://www.pluralsight.com/guides/microsoft-net/building-a-generic-csv-writer-reader-using-reflection

C# Add element to an Object List using variable as List key

Let me explain, I have a model list in which I have a little more than a thousand parameters, so I have to fill the list with some variables, the thing is that I don't want to do this:
list.Add(new Model{
name1= value,
name2= value,
.....
name1000=value
});
I have an array that contains the names of the parameters in the list, so I was wondering if is possible to use that array of the names and in a loop get the variables fill in, something like this:
list.Add(new Model{
//a loop?
array[0]= value
});
Thanks.
You can achieve this using reflection. Code below
public class ModelFactory
{
private IDictionary<string, PropertyInfo> propertiesInfo { get; set; }
public ModelFactory()
{
this.propertiesInfo = typeof(Model)
.GetProperties()
.ToDictionary(p => p.Name, p => p);
}
public Model Create(string[] propertiesToInitialize, dynamic value)
{
var model = new Model();
foreach (var propertyName in propertiesToInitialize)
{
if (this.propertiesInfo.ContainsKey(propertyName))
{
var property = this.propertiesInfo[propertyName];
property.SetValue(model, value);
}
}
return model;
}
}
Model to initialize
public class Model
{
public int MyProperty1 { get; set; }
public int MyProperty2 { get; set; }
public int MyProperty3 { get; set; }
public int MyProperty4 { get; set; }
public int MyProperty5 { get; set; }
}
Usage
public void Test()
{
var propertiesToInitialize = new string[] { "MyProperty1", "MyProperty2", "MyProperty4" };
var modelFactory = new ModelFactory();
var list = new List<Model>();
list.Add(modelFactory.Create(propertiesToInitialize, 500));
Console.WriteLine("MyProperty1 " + list[0].MyProperty1); // 500
Console.WriteLine("MyProperty2 " + list[0].MyProperty2); // 500
Console.WriteLine("MyProperty3 " + list[0].MyProperty3); // 0
Console.WriteLine("MyProperty4 " + list[0].MyProperty4); // 500
Console.WriteLine("MyProperty5 " + list[0].MyProperty5); // 0
}
However as already mentioned in comments, please reconsider your model design because model with these many properties is not optimal.

How to re-factor a spec flow data table and a Test Context properties

Field Length position Value
TRANS LENGTH 4 5 1234
TRANS DEST 7 9 DEV
TRANS ORGN 7 16 PROD
TRANS DATE 6 23 2014-03-30
TRANS ID 4 35 44
read table
Here I am using the field as a Key and {length, position, and value} as a value
public class SpecFlowData
{
public string Field { get; set; }
public string Value { get; set; }
public string Position { get; set; }
public string Length { get; set; }
public Dictionary<string, SpecFlowData> GetData(Table table)
{
var data = table.CreateSet<SpecFlowData>().ToList();
var result = data
.ToDictionary(x => x.Field, x => new SpecFlowData
{
Value = x.Value,
Position = x.Position,
Length = x.Length
});
return result;
}
}
public class TestContext
{
public SpecFlowData TRANSLENGETH{ get; set; }
public SpecFlowData TRANSDEST{ get; set; }
public SpecFlowData TRANSORGN { get; set; }
public SpecFlowData TRANSDATE { get; set; }
public SpecFlowData TRANSID { get; set; }
}
here I am trying to populate the values so that I can do my assertion
[Given(#"Request is sent with the following data:")]
public void GivenRequestIsSentWithTheFollowingData(Table table)
{
var data = SpecFlowData.GetRequestData(table);
TestContext.TRANSLENGETH= data["TRANSLENGETH"];
TestContext.TRANSDEST= data["TRANSDEST"];
TestContext.TRANSORGN = data["TRANSORGN "];
}
[Then(#"Response will contain the expected data:")]
public void ThenResponseWillContainTheExpectedData()
{
var Response = "HERE I HAVE the string response message that I am asserting ";
Assert.AreEqual(Response.Substring(TestContext.TransLength.Position, TestContext.TransLength.Length), TestContext.TransLength);
Assert.AreEqual(Response.Substring(TestContext.TransDest.Position, TestContext.TransDest.Length), TestContext.TransDest);
...... more TestContext properties to be asserted.
}
But this seams like a redundant code. How can I re-factor it?
You can simply use the indexer to get your values, since you have a Dictionary, you don't need to use FirstOrDefault method, just specify the key and then the index of the value:
string value = data["TRANS ID"][0];
string position = data["TRANS ID"][1];
string length = data["TRANS ID"][2];
But, I would use a Dictionary<string, SpecFlowData> instead of Dictionary<string, IList<string>> then change the method like this:
public Dictionary<string, SpecFlowData> GetData(Table table)
{
var data = table.CreateSet<SpecFlowData>().ToList();
var result = data
.ToDictionary(x => x.Field, x => new SpecFlowData
{
Value = x.Value,
Position = x.Position,
Length = x.Length
});
return result;
}
Then get the values like this:
string value = data["TRANS ID"].Value;
string position = data["TRANS ID"].Position;
string length = data["TRANS ID"].Length;

Categories