Reputation: 53
New to csvHelper. I'd like to delete empty columns from csv file. I'm using CsvWriter to write the csv row definition class to stream.
Here is my code:
var records = getRecords(); // list of row definition items
var stream = new MemoryStream();
var writer = new StreamWriter(stream);
using (var csv = new CsvWriter(writer))
{
csv.WriteRecords(records);
writer.Flush();
return stream;
}
Is there any way to make CsvWriter ignore empty columns? maybe configuration?
actual csv:
First Name | Middle Name| Last Name |Gender
FName1 | | LName1 | Male
FName2 | | LName2 | Female
FName3 | | LName3 |
expected csv:
First Name | Last Name |Gender
FName1 | LName1 | Male
FName2 | LName2 | Female
FName3 | LName3 |
Upvotes: 3
Views: 2785
Reputation: 404
A more generic solution would be to convert your records into a List<dynamic>
and use reflection to build each element in that list. Then examine what columns are used in this list (what props occur in it), create a second list in which every used property is filled with a value, null if no value. Then dump that list into csv. A little convoluted but works :)
var firstSort = new List<dynamic>();
var secondSort = new List<dynamic>();
//firstly convert data into dynamic objects
foreach (var rec in records)
{
dynamic dRec = new ExpandoObject();
var dataDictionary = (IDictionary<string, object>)dRec;
//add properties
foreach (var propertyInfo in rec.GetType().GetProperties())
{
var propertyName = propertyInfo.Name;
var propertyValue = propertyInfo.GetValue(rec);
dataDictionary.Add(propertyName, propertyValue);
}
firstSort.Add(dRec)
}
//get all resulting columns
var columns = new HashSet<string>();
firstSort.ForEach(r => columns.UnionWith((r as IDictionary<string, object>).Keys));
//fill all props (columns) for every row
foreach (IDictionary<string, object> rOld in firstSort)
{
var rNewDynamic = (IDictionary<string, object>)(new ExpandoObject());
foreach (var c in columns)
{
if (!rOld.ContainsKey(c))
rNewDynamic.Add(c, null);
else
rNewDynamic[c] = rOld[c];
}
secondSort.Add(rNewDynamic);
}
//dump the secondSort list into the csv
csvWriter.WriteRecords(secondSort);
That assures each property value ends up in proper column for every row.
Upvotes: 0
Reputation: 9094
It is possible to use configuration to accomplish this.
public static void Main(string[] args)
{
var records = new List<Foo>
{
new Foo { FirstName = "FName1", LastName = "LName1", Gender = "Male"},
new Foo { FirstName = "FName2", LastName = "LName2", Gender = "Female"},
new Foo { FirstName = "FName3", LastName = "LName3"},
};
using (var csv = new CsvWriter(Console.Out))
{
var map = new FooMap();
var properties = typeof(Foo).GetProperties();
foreach (var property in properties)
{
if(records.All(foo => property.GetValue(foo) == null))
{
map.Map(typeof(Foo), property).Ignore();
}
}
csv.Configuration.RegisterClassMap(map);
csv.WriteRecords(records);
}
Console.ReadKey();
}
public class Foo
{
public string FirstName { get; set; }
public string MiddleName { get; set; }
public string LastName { get; set; }
public string Gender { get; set; }
}
public class FooMap : ClassMap<Foo>
{
public FooMap()
{
Map(m => m.FirstName).Name("First Name");
Map(m => m.MiddleName).Name("Middle Name");
Map(m => m.LastName).Name("Last Name");
Map(m => m.Gender);
}
}
And if you didn't want to create a separate class map, just replace FooMap
with DefaultClassMap<Foo>
and use AutoMap
.
var map = new DefaultClassMap<Foo>();
map.AutoMap();
Upvotes: 4
Reputation: 2226
Project records to a list of objects that do not have the middle name property. Something like this (using linq)
var projectedRecords = records.Select(r => new PersonDto
{
FirstName = r.FirstName,
LastName = r.LastName,
Gender = r.Gender
}).ToList();
Then pass projectedRecords to the csvWriter.
Upvotes: 0