Convert IEnumerable to CSV

Clash Royale CLAN TAG#URR8PPP
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;
up vote
5
down vote
favorite
I have an IEnumerable of a view model that I need to convert to CSV and send back as a file response. I have written it out quite manually and I'm just seeking code review and suggestions on a more elegant solution, or alternatively if this looks acceptable, confirmation of that:
 public ActionResult ExportBasicReads(string nmi, DateTime fromDate, DateTime toDate)
 
 var reads = _dataService.GetBasicReads(nmi, fromDate, toDate)
 .Where(r => r.CurrentReadQuality != ReadQuality.Estimate)
 .OrderBy(r => r.CurrentReadDate)
 .ThenBy(r => r.DeviceKey)
 .ThenBy(r => r.ValidationCode);
 StringBuilder csv = new StringBuilder();
 csv.AppendLine("Read Date,Device : Suffix,Read,Quality,Tran Code,Service Order,Previous Read Date,Previous Read,Previous Quality," +
 "Previous Tran Code,Dial Diff,Quantity,UOM,Dial Factor,Direction,Validation,From Date,To Date,Kwh,Avg Daily Load,Profile Name," +
 "Profle Area,Reading Timestamp,Update Date,ChangedBy,ChangeType,ChangeComment");
 reads.ForEach(x => csv.AppendLine(
 string.Format("0,1 : 2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22," +
 "23,24,25,26,27",
 x.CurrentReadDate,
 x.DeviceKey,
 x.Suffix,
 x.CurrentReadRawValue,
 x.CurrentReadQualityDesc,
 x.CurrentReadTransactionCode,
 x.CurrentServiceOrder,
 x.PreviousReadDate,
 x.PreviousReadRawValue,
 x.PreviousReadQualityDesc,
 x.PreviousReadTransactionCode,
 x.DialDifference,
 x.Quantity,
 x.UOMDesc,
 x.Multiplier,
 x.DirectionDesc,
 x.ValidationDesc,
 x.EffectiveFromDateTime,
 x.EffectiveToDateTime,
 x.Kwh,
 x.AverageDailyLoad,
 x.ProfileName,
 x.ProfileArea,
 x.EffectiveFromDateTime,
 x.UpdateTimestamp,
 x.ChangedBy,
 x.ChangeType,
 x.ChangeComment
 )
 )); 
 return File(Encoding.ASCII.GetBytes(csv.ToString()), "text/csv", "BasicReadsExport.csv");
 
c# mvc csv asp.net
add a comment |Â
up vote
5
down vote
favorite
I have an IEnumerable of a view model that I need to convert to CSV and send back as a file response. I have written it out quite manually and I'm just seeking code review and suggestions on a more elegant solution, or alternatively if this looks acceptable, confirmation of that:
 public ActionResult ExportBasicReads(string nmi, DateTime fromDate, DateTime toDate)
 
 var reads = _dataService.GetBasicReads(nmi, fromDate, toDate)
 .Where(r => r.CurrentReadQuality != ReadQuality.Estimate)
 .OrderBy(r => r.CurrentReadDate)
 .ThenBy(r => r.DeviceKey)
 .ThenBy(r => r.ValidationCode);
 StringBuilder csv = new StringBuilder();
 csv.AppendLine("Read Date,Device : Suffix,Read,Quality,Tran Code,Service Order,Previous Read Date,Previous Read,Previous Quality," +
 "Previous Tran Code,Dial Diff,Quantity,UOM,Dial Factor,Direction,Validation,From Date,To Date,Kwh,Avg Daily Load,Profile Name," +
 "Profle Area,Reading Timestamp,Update Date,ChangedBy,ChangeType,ChangeComment");
 reads.ForEach(x => csv.AppendLine(
 string.Format("0,1 : 2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22," +
 "23,24,25,26,27",
 x.CurrentReadDate,
 x.DeviceKey,
 x.Suffix,
 x.CurrentReadRawValue,
 x.CurrentReadQualityDesc,
 x.CurrentReadTransactionCode,
 x.CurrentServiceOrder,
 x.PreviousReadDate,
 x.PreviousReadRawValue,
 x.PreviousReadQualityDesc,
 x.PreviousReadTransactionCode,
 x.DialDifference,
 x.Quantity,
 x.UOMDesc,
 x.Multiplier,
 x.DirectionDesc,
 x.ValidationDesc,
 x.EffectiveFromDateTime,
 x.EffectiveToDateTime,
 x.Kwh,
 x.AverageDailyLoad,
 x.ProfileName,
 x.ProfileArea,
 x.EffectiveFromDateTime,
 x.UpdateTimestamp,
 x.ChangedBy,
 x.ChangeType,
 x.ChangeComment
 )
 )); 
 return File(Encoding.ASCII.GetBytes(csv.ToString()), "text/csv", "BasicReadsExport.csv");
 
c# mvc csv asp.net
2
You can use string interpolation instead ofstring.Format()?
â vasily.sib
Jun 22 at 5:37
2
Looks pretty clean to me.
â paparazzo
Jun 22 at 12:21
1
No need to reinvent - the popular ServiceStack.Text NuGet package has an extension method called.ToCsv(): docs.servicestack.net/csv-format
â Jesse C. Slicer
Jun 22 at 14:35
1
thestring.Formatis awfully hard to follow given the large number of parameters. I would suggest something like$"x.CurrentReadDate,x.DeviceKey" .... This would also help avoid errors due to matching up parameter indexes if this code was later modified.
â Zer0
Jun 23 at 4:09
add a comment |Â
up vote
5
down vote
favorite
up vote
5
down vote
favorite
I have an IEnumerable of a view model that I need to convert to CSV and send back as a file response. I have written it out quite manually and I'm just seeking code review and suggestions on a more elegant solution, or alternatively if this looks acceptable, confirmation of that:
 public ActionResult ExportBasicReads(string nmi, DateTime fromDate, DateTime toDate)
 
 var reads = _dataService.GetBasicReads(nmi, fromDate, toDate)
 .Where(r => r.CurrentReadQuality != ReadQuality.Estimate)
 .OrderBy(r => r.CurrentReadDate)
 .ThenBy(r => r.DeviceKey)
 .ThenBy(r => r.ValidationCode);
 StringBuilder csv = new StringBuilder();
 csv.AppendLine("Read Date,Device : Suffix,Read,Quality,Tran Code,Service Order,Previous Read Date,Previous Read,Previous Quality," +
 "Previous Tran Code,Dial Diff,Quantity,UOM,Dial Factor,Direction,Validation,From Date,To Date,Kwh,Avg Daily Load,Profile Name," +
 "Profle Area,Reading Timestamp,Update Date,ChangedBy,ChangeType,ChangeComment");
 reads.ForEach(x => csv.AppendLine(
 string.Format("0,1 : 2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22," +
 "23,24,25,26,27",
 x.CurrentReadDate,
 x.DeviceKey,
 x.Suffix,
 x.CurrentReadRawValue,
 x.CurrentReadQualityDesc,
 x.CurrentReadTransactionCode,
 x.CurrentServiceOrder,
 x.PreviousReadDate,
 x.PreviousReadRawValue,
 x.PreviousReadQualityDesc,
 x.PreviousReadTransactionCode,
 x.DialDifference,
 x.Quantity,
 x.UOMDesc,
 x.Multiplier,
 x.DirectionDesc,
 x.ValidationDesc,
 x.EffectiveFromDateTime,
 x.EffectiveToDateTime,
 x.Kwh,
 x.AverageDailyLoad,
 x.ProfileName,
 x.ProfileArea,
 x.EffectiveFromDateTime,
 x.UpdateTimestamp,
 x.ChangedBy,
 x.ChangeType,
 x.ChangeComment
 )
 )); 
 return File(Encoding.ASCII.GetBytes(csv.ToString()), "text/csv", "BasicReadsExport.csv");
 
c# mvc csv asp.net
I have an IEnumerable of a view model that I need to convert to CSV and send back as a file response. I have written it out quite manually and I'm just seeking code review and suggestions on a more elegant solution, or alternatively if this looks acceptable, confirmation of that:
 public ActionResult ExportBasicReads(string nmi, DateTime fromDate, DateTime toDate)
 
 var reads = _dataService.GetBasicReads(nmi, fromDate, toDate)
 .Where(r => r.CurrentReadQuality != ReadQuality.Estimate)
 .OrderBy(r => r.CurrentReadDate)
 .ThenBy(r => r.DeviceKey)
 .ThenBy(r => r.ValidationCode);
 StringBuilder csv = new StringBuilder();
 csv.AppendLine("Read Date,Device : Suffix,Read,Quality,Tran Code,Service Order,Previous Read Date,Previous Read,Previous Quality," +
 "Previous Tran Code,Dial Diff,Quantity,UOM,Dial Factor,Direction,Validation,From Date,To Date,Kwh,Avg Daily Load,Profile Name," +
 "Profle Area,Reading Timestamp,Update Date,ChangedBy,ChangeType,ChangeComment");
 reads.ForEach(x => csv.AppendLine(
 string.Format("0,1 : 2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22," +
 "23,24,25,26,27",
 x.CurrentReadDate,
 x.DeviceKey,
 x.Suffix,
 x.CurrentReadRawValue,
 x.CurrentReadQualityDesc,
 x.CurrentReadTransactionCode,
 x.CurrentServiceOrder,
 x.PreviousReadDate,
 x.PreviousReadRawValue,
 x.PreviousReadQualityDesc,
 x.PreviousReadTransactionCode,
 x.DialDifference,
 x.Quantity,
 x.UOMDesc,
 x.Multiplier,
 x.DirectionDesc,
 x.ValidationDesc,
 x.EffectiveFromDateTime,
 x.EffectiveToDateTime,
 x.Kwh,
 x.AverageDailyLoad,
 x.ProfileName,
 x.ProfileArea,
 x.EffectiveFromDateTime,
 x.UpdateTimestamp,
 x.ChangedBy,
 x.ChangeType,
 x.ChangeComment
 )
 )); 
 return File(Encoding.ASCII.GetBytes(csv.ToString()), "text/csv", "BasicReadsExport.csv");
 
c# mvc csv asp.net
edited Jun 22 at 14:22
Jesse C. Slicer
10.9k2738
10.9k2738
asked Jun 22 at 4:02
TDSnet
283
283
2
You can use string interpolation instead ofstring.Format()?
â vasily.sib
Jun 22 at 5:37
2
Looks pretty clean to me.
â paparazzo
Jun 22 at 12:21
1
No need to reinvent - the popular ServiceStack.Text NuGet package has an extension method called.ToCsv(): docs.servicestack.net/csv-format
â Jesse C. Slicer
Jun 22 at 14:35
1
thestring.Formatis awfully hard to follow given the large number of parameters. I would suggest something like$"x.CurrentReadDate,x.DeviceKey" .... This would also help avoid errors due to matching up parameter indexes if this code was later modified.
â Zer0
Jun 23 at 4:09
add a comment |Â
2
You can use string interpolation instead ofstring.Format()?
â vasily.sib
Jun 22 at 5:37
2
Looks pretty clean to me.
â paparazzo
Jun 22 at 12:21
1
No need to reinvent - the popular ServiceStack.Text NuGet package has an extension method called.ToCsv(): docs.servicestack.net/csv-format
â Jesse C. Slicer
Jun 22 at 14:35
1
thestring.Formatis awfully hard to follow given the large number of parameters. I would suggest something like$"x.CurrentReadDate,x.DeviceKey" .... This would also help avoid errors due to matching up parameter indexes if this code was later modified.
â Zer0
Jun 23 at 4:09
2
2
You can use string interpolation instead of
string.Format()?â vasily.sib
Jun 22 at 5:37
You can use string interpolation instead of
string.Format()?â vasily.sib
Jun 22 at 5:37
2
2
Looks pretty clean to me.
â paparazzo
Jun 22 at 12:21
Looks pretty clean to me.
â paparazzo
Jun 22 at 12:21
1
1
No need to reinvent - the popular ServiceStack.Text NuGet package has an extension method called
.ToCsv(): docs.servicestack.net/csv-formatâ Jesse C. Slicer
Jun 22 at 14:35
No need to reinvent - the popular ServiceStack.Text NuGet package has an extension method called
.ToCsv(): docs.servicestack.net/csv-formatâ Jesse C. Slicer
Jun 22 at 14:35
1
1
the
string.Format is awfully hard to follow given the large number of parameters. I would suggest something like $"x.CurrentReadDate,x.DeviceKey" .... This would also help avoid errors due to matching up parameter indexes if this code was later modified.â Zer0
Jun 23 at 4:09
the
string.Format is awfully hard to follow given the large number of parameters. I would suggest something like $"x.CurrentReadDate,x.DeviceKey" .... This would also help avoid errors due to matching up parameter indexes if this code was later modified.â Zer0
Jun 23 at 4:09
add a comment |Â
 1 Answer
 1
 
active
oldest
votes
up vote
4
down vote
accepted
I modified the formatting part for better readability and maintainability, at the cost of lower performance:
var columns = new Dictionary<string, Func<Reads, object>>
 ["Read Date"] = x => x.CurrentReadDate,
 ["Device"] = x => x.DeviceKey,
 ["Suffix"] = x => x.Suffix,
 ["Read"] = x => x.CurrentReadRawValue,
 // add the rest of headers/property selectors below
;
//var format = "0,1 : 2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27";
var format = "01 : " + string.Join(",", columns.Select((_, i) => $"i").Skip(2));
var csv = new StringBuilder()
 .AppendFormat(format, columns.Keys.ToArray())
 .AppendLine();
foreach (var read in reads)
 var values = columns.Values.Select(selector => selector(read));
 csv
 .AppendFormat(format, values.ToArray())
 .AppendLine();
The advantage of this approach is that you can add/move/remove the columns without having to worry about updating the header and the format, and if they are kept in sync or not.
Note:
- You can also revert to the constant format if it becomes more complex.
 - The property selector can be chained if you need special format on individual property, like: 
x => x.CurrentReadDate.ToString("yyyyMMddThhmm") 
add a comment |Â
 1 Answer
 1
 
active
oldest
votes
 1 Answer
 1
 
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
4
down vote
accepted
I modified the formatting part for better readability and maintainability, at the cost of lower performance:
var columns = new Dictionary<string, Func<Reads, object>>
 ["Read Date"] = x => x.CurrentReadDate,
 ["Device"] = x => x.DeviceKey,
 ["Suffix"] = x => x.Suffix,
 ["Read"] = x => x.CurrentReadRawValue,
 // add the rest of headers/property selectors below
;
//var format = "0,1 : 2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27";
var format = "01 : " + string.Join(",", columns.Select((_, i) => $"i").Skip(2));
var csv = new StringBuilder()
 .AppendFormat(format, columns.Keys.ToArray())
 .AppendLine();
foreach (var read in reads)
 var values = columns.Values.Select(selector => selector(read));
 csv
 .AppendFormat(format, values.ToArray())
 .AppendLine();
The advantage of this approach is that you can add/move/remove the columns without having to worry about updating the header and the format, and if they are kept in sync or not.
Note:
- You can also revert to the constant format if it becomes more complex.
 - The property selector can be chained if you need special format on individual property, like: 
x => x.CurrentReadDate.ToString("yyyyMMddThhmm") 
add a comment |Â
up vote
4
down vote
accepted
I modified the formatting part for better readability and maintainability, at the cost of lower performance:
var columns = new Dictionary<string, Func<Reads, object>>
 ["Read Date"] = x => x.CurrentReadDate,
 ["Device"] = x => x.DeviceKey,
 ["Suffix"] = x => x.Suffix,
 ["Read"] = x => x.CurrentReadRawValue,
 // add the rest of headers/property selectors below
;
//var format = "0,1 : 2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27";
var format = "01 : " + string.Join(",", columns.Select((_, i) => $"i").Skip(2));
var csv = new StringBuilder()
 .AppendFormat(format, columns.Keys.ToArray())
 .AppendLine();
foreach (var read in reads)
 var values = columns.Values.Select(selector => selector(read));
 csv
 .AppendFormat(format, values.ToArray())
 .AppendLine();
The advantage of this approach is that you can add/move/remove the columns without having to worry about updating the header and the format, and if they are kept in sync or not.
Note:
- You can also revert to the constant format if it becomes more complex.
 - The property selector can be chained if you need special format on individual property, like: 
x => x.CurrentReadDate.ToString("yyyyMMddThhmm") 
add a comment |Â
up vote
4
down vote
accepted
up vote
4
down vote
accepted
I modified the formatting part for better readability and maintainability, at the cost of lower performance:
var columns = new Dictionary<string, Func<Reads, object>>
 ["Read Date"] = x => x.CurrentReadDate,
 ["Device"] = x => x.DeviceKey,
 ["Suffix"] = x => x.Suffix,
 ["Read"] = x => x.CurrentReadRawValue,
 // add the rest of headers/property selectors below
;
//var format = "0,1 : 2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27";
var format = "01 : " + string.Join(",", columns.Select((_, i) => $"i").Skip(2));
var csv = new StringBuilder()
 .AppendFormat(format, columns.Keys.ToArray())
 .AppendLine();
foreach (var read in reads)
 var values = columns.Values.Select(selector => selector(read));
 csv
 .AppendFormat(format, values.ToArray())
 .AppendLine();
The advantage of this approach is that you can add/move/remove the columns without having to worry about updating the header and the format, and if they are kept in sync or not.
Note:
- You can also revert to the constant format if it becomes more complex.
 - The property selector can be chained if you need special format on individual property, like: 
x => x.CurrentReadDate.ToString("yyyyMMddThhmm") 
I modified the formatting part for better readability and maintainability, at the cost of lower performance:
var columns = new Dictionary<string, Func<Reads, object>>
 ["Read Date"] = x => x.CurrentReadDate,
 ["Device"] = x => x.DeviceKey,
 ["Suffix"] = x => x.Suffix,
 ["Read"] = x => x.CurrentReadRawValue,
 // add the rest of headers/property selectors below
;
//var format = "0,1 : 2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27";
var format = "01 : " + string.Join(",", columns.Select((_, i) => $"i").Skip(2));
var csv = new StringBuilder()
 .AppendFormat(format, columns.Keys.ToArray())
 .AppendLine();
foreach (var read in reads)
 var values = columns.Values.Select(selector => selector(read));
 csv
 .AppendFormat(format, values.ToArray())
 .AppendLine();
The advantage of this approach is that you can add/move/remove the columns without having to worry about updating the header and the format, and if they are kept in sync or not.
Note:
- You can also revert to the constant format if it becomes more complex.
 - The property selector can be chained if you need special format on individual property, like: 
x => x.CurrentReadDate.ToString("yyyyMMddThhmm") 
answered Jun 22 at 18:07
Xiaoy312
2,767915
2,767915
add a comment |Â
add a comment |Â
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f197025%2fconvert-ienumerablereads-to-csv%23new-answer', 'question_page');
);
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
2
You can use string interpolation instead of
string.Format()?â vasily.sib
Jun 22 at 5:37
2
Looks pretty clean to me.
â paparazzo
Jun 22 at 12:21
1
No need to reinvent - the popular ServiceStack.Text NuGet package has an extension method called
.ToCsv(): docs.servicestack.net/csv-formatâ Jesse C. Slicer
Jun 22 at 14:35
1
the
string.Formatis awfully hard to follow given the large number of parameters. I would suggest something like$"x.CurrentReadDate,x.DeviceKey" .... This would also help avoid errors due to matching up parameter indexes if this code was later modified.â Zer0
Jun 23 at 4:09