Last active
December 27, 2015 00:59
-
-
Save davetransom/7241982 to your computer and use it in GitHub Desktop.
A simple, lightweight and maintainable way to output arbitrary CSV/TSV - keeps header names close to the column/value definition.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// vars to be used inside column value funcs | |
DateTime utcnow = DateTime.UtcNow; | |
const decimal VIP_SPEND = 1000000M; | |
var fields = new CsvDefinition<MyRecord> | |
{ | |
{ "Username", row => string.Concat(row.Prefix, ":", row.Username) }, | |
{ "Email", row => row.Email }, | |
{ "Status", row => row.TotalSpend >= VIP_SPEND ? "VIP" : "Pffft..., Peon" }, | |
{ "Spend ($)", row => row.TotalSpend == null ? "None" : row.TotalSpend.ToString("n2") }, | |
{ "Year of Birth", row => row.Yob > 1900 && row.Yob <= utcnow.Year ? row.Yob.Value.ToString() : null }, | |
{ "Enabled", row => row.IsEnabled ? "Y" : "N" }, | |
{ "Flags", row => string.Join(", ", new string[] | |
{ | |
row.IsDemo ? "demo" : null, | |
row.IsManager ? "manager" : null, | |
row.IsAdmin ? "admin" : null | |
}.Where(x => x != null)) | |
}, | |
{ "Timestamp", row => DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss.fff") } | |
}; | |
IEnumerable<MyRecord> rows = someDataSource.AsEnumerable<MyRecord>(); | |
using (var writer = new StringWriter()) | |
{ | |
// perhaps write out some additional notes or headers | |
// writer.WriteLine("# -- bof --") | |
fields.Write(writer, rows, "\t"); // tab delimited | |
// writer.WriteLine("# -- eof --") | |
return writer.ToString(); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class CsvDefinition<T> : List<KeyValuePair<string, Func<T, string>>> | |
{ | |
public CsvDefinition() | |
: base() | |
{ } | |
public CsvDefinition(int capacity) | |
: base(capacity) | |
{ } | |
public CsvDefinition(IEnumerable<KeyValuePair<string, Func<T, string>>> items) | |
: base(items) | |
{ } | |
/// <summary> | |
/// Adds a Key/Value pair to the definition - used for easy object initialisation | |
/// </summary> | |
/// <param name="key">The name of the column/header</param> | |
/// <param name="value">The Func to convert T into a string for the column value</param> | |
public void Add(string key, Func<T, string> value) | |
{ | |
Add(new KeyValuePair<string, Func<T, string>>(key, value)); | |
} | |
/// <summary> | |
/// Writes the CSV, outputs a header line, then iterates over rows, converting into column values using the defined func | |
/// </summary> | |
/// <param name="writer">The TextWriter to write the lines to</param> | |
/// <param name="rows">The enumerable of T tothat represents the lines</param> | |
/// <param name="delimiter">The string to delimit column values with. A single character delimiter isare also used to escape the value, multi-character strings are not escaped.</param> | |
public void Write(TextWriter writer, IEnumerable<T> rows, string delimiter = ",") | |
{ | |
if(delimiter == null) | |
throw new ArgumentNullException("delimiter"); | |
char[] escChars = delimiter.Length == 1 ? new[] { delimiter[0], '\n', '\r', '"' } : new[] { '\n', '\r', '"' }; | |
string header = string.Join(delimiter, this.Select(x => escape(x.Key, escChars))); | |
writer.WriteLine(header); | |
foreach (T row in rows) | |
{ | |
string line = string.Join( | |
delimiter, | |
this.Select(x => escape(x.Value(row), escChars)) // x.Value == Func<T, string> | |
); | |
writer.WriteLine(line); | |
} | |
} | |
// adapted from: http://www.asp.net/web-api/overview/formats-and-model-binding/media-formatters | |
static string escape(string value, char[] chars) | |
{ | |
if (value == null) | |
return null; | |
if (value.IndexOfAny(chars) != -1) | |
return string.Concat("\"", value.Replace("\"", "\"\""), "\""); | |
return value; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment