Skip to content

Instantly share code, notes, and snippets.

@dibley1973
Last active November 8, 2015 22:19
Show Gist options
  • Save dibley1973/3032d1c33f18b188d51a to your computer and use it in GitHub Desktop.
Save dibley1973/3032d1c33f18b188d51a to your computer and use it in GitHub Desktop.
ToDelimitedText() extension for List<T>
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
namespace Gists.Extensions.ListOfTExtentions
{
public static class ListOfTExtentions
{
/// <summary>
/// Converst this instance to delimited text.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="instance">The instance.</param>
/// <param name="delimiter">The delimiter.</param>
/// <param name="includeHeader">
/// if set to <c>true</c> then the header row is included.
/// </param>
/// <param name="trimTrailingNewLineIfExists">
/// If set to <c>true</c> then trim trailing new line if it exists.
/// </param>
/// <returns></returns>
public static string ToDelimitedText<T>(this List<T> instance,
string delimiter,
bool includeHeader = false,
bool trimTrailingNewLineIfExists = false)
where T : class, new()
{
int itemCount = instance.Count;
if (itemCount == 0) return string.Empty;
var properties = GetPropertiesOfType<T>();
int propertyCount = properties.Length;
var outputBuilder = new StringBuilder();
AddHeaderIfRequired(outputBuilder, includeHeader, properties, propertyCount, delimiter);
for (int itemIndex = 0; itemIndex < itemCount; itemIndex++)
{
T listItem = instance[itemIndex];
AppendListItemToOutputBuilder(outputBuilder, listItem, properties, propertyCount, delimiter);
AddNewLineIfRequired(trimTrailingNewLineIfExists, itemIndex, itemCount, outputBuilder);
}
var output = outputBuilder.ToString();
return output;
}
private static void AddHeaderIfRequired(StringBuilder outputBuilder,
bool includeHeader,
PropertyInfo[] properties,
int propertyCount,
string delimiter)
{
if (!includeHeader) return;
for (int propertyIndex = 0; propertyIndex < properties.Length; propertyIndex += 1)
{
var property = properties[propertyIndex];
var propertyName = property.Name;
outputBuilder.Append(propertyName);
AddDelimiterIfRequired(outputBuilder, propertyCount, delimiter, propertyIndex);
}
outputBuilder.Append(Environment.NewLine);
}
private static void AddDelimiterIfRequired(StringBuilder outputBuilder, int propertyCount, string delimiter,
int propertyIndex)
{
bool isLastProperty = (propertyIndex + 1 == propertyCount);
if (!isLastProperty)
{
outputBuilder.Append(delimiter);
}
}
private static void AddNewLineIfRequired(bool trimTrailingNewLineIfExists, int itemIndex, int itemCount,
StringBuilder outputBuilder)
{
bool isLastItem = (itemIndex + 1 == itemCount);
if (!isLastItem || !trimTrailingNewLineIfExists)
{
outputBuilder.Append(Environment.NewLine);
}
}
private static void AppendListItemToOutputBuilder<T>(StringBuilder outputBuilder,
T listItem,
PropertyInfo[] properties,
int propertyCount,
string delimiter)
where T : class, new()
{
for (int propertyIndex = 0; propertyIndex < properties.Length; propertyIndex += 1)
{
var property = properties[propertyIndex];
var propertyValue = property.GetValue(listItem);
outputBuilder.Append(propertyValue);
AddDelimiterIfRequired(outputBuilder, propertyCount, delimiter, propertyIndex);
}
}
private static PropertyInfo[] GetPropertiesOfType<T>() where T : class, new()
{
Type itemType = typeof(T);
var properties = itemType.GetProperties(BindingFlags.Instance | BindingFlags.GetProperty | BindingFlags.Public);
return properties;
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Gists.Extensions.ListOfTExtentions;
namespace Gists_Tests.ExtensionTests.ListOfTExtentionTests
{
[TestClass]
public class ListOfT_ToDelimitedTextTests
{
#region Mock Data
private class ComplexObject
{
public int Id { get; set; }
public string Name { get; set; }
public bool Active { get; set; }
}
#endregion
#region Tests
[TestMethod]
public void ToDelimitedText_ReturnsCorrectNumberOfRows()
{
// ARRANGE
var itemList = new List<ComplexObject>
{
new ComplexObject {Id = 1, Name = "Sid", Active = true},
new ComplexObject {Id = 2, Name = "James", Active = false},
new ComplexObject {Id = 3, Name = "Ted", Active = true}
};
const string delimiter = ",";
const int expectedRowCount = 3;
const bool includeHeader = false;
const bool trimTrailingNewLineIfExists = true;
// ACT
string result = itemList.ToDelimitedText(delimiter, includeHeader, trimTrailingNewLineIfExists);
var lines = result.Split(new[] { Environment.NewLine }, StringSplitOptions.None);
var actualRowCount = lines.Length;
// ASSERT
Assert.AreEqual(expectedRowCount, actualRowCount);
}
[TestMethod]
public void ToDelimitedText_IncludesHeaderRow_WhenSet()
{
// ARRANGE
var itemList = new List<ComplexObject>
{
new ComplexObject {Id = 1, Name = "Sid", Active = true},
new ComplexObject {Id = 2, Name = "James", Active = false},
new ComplexObject {Id = 3, Name = "Ted", Active = true}
};
const string delimiter = ",";
const int expectedRowCount = 4;
const bool includeHeader = true;
const bool trimTrailingNewLineIfExists = true;
const string expectedHeader = @"Id,Name,Active";
// ACT
string result = itemList.ToDelimitedText(delimiter, includeHeader, trimTrailingNewLineIfExists);
var lines = result.Split(new[] { Environment.NewLine }, StringSplitOptions.None);
int actualRowCount = lines.Length;
string actualFirstRow = lines[0];
// ASSERT
Assert.AreEqual(expectedRowCount, actualRowCount);
Assert.AreEqual(expectedHeader, actualFirstRow);
}
[TestMethod]
public void ToDelimitedText_ReturnsCorrectNumberOfProperties()
{
// ARRANGE
var itemList = new List<ComplexObject>
{
new ComplexObject {Id = 1, Name = "Sid", Active = true}
};
const string delimiter = ",";
const int expectedPropertyCount = 3;
// ACT
string result = itemList.ToDelimitedText(delimiter);
var lines = result.Split(new[] { Environment.NewLine }, StringSplitOptions.None);
var properties = lines.First().Split(delimiter.ToCharArray());
var actualPropertyCount = properties.Length;
// ASSERT
Assert.AreEqual(expectedPropertyCount, actualPropertyCount);
}
[TestMethod]
public void ToDelimitedText_RemovesTrailingNewLine_WhenSet()
{
// ARRANGE
var itemList = new List<ComplexObject>
{
new ComplexObject {Id = 1, Name = "Sid", Active = true},
new ComplexObject {Id = 2, Name = "James", Active = false},
new ComplexObject {Id = 3, Name = "Ted", Active = true},
};
const string delimiter = ",";
const bool includeHeader = false;
const bool trimTrailingNewLineIfExists = true;
// ACT
string result = itemList.ToDelimitedText(delimiter, includeHeader,trimTrailingNewLineIfExists);
bool endsWithNewLine = result.EndsWith(Environment.NewLine);
// ASSERT
Assert.IsFalse(endsWithNewLine);
}
[TestMethod]
public void ToDelimitedText_IncludesTrailingNewLine_WhenNotSet()
{
// ARRANGE
var itemList = new List<ComplexObject>
{
new ComplexObject {Id = 1, Name = "Sid", Active = true},
new ComplexObject {Id = 2, Name = "James", Active = false},
new ComplexObject {Id = 3, Name = "Ted", Active = true},
};
const string delimiter = ",";
// ACT
string result = itemList.ToDelimitedText(delimiter);
bool endsWithNewLine = result.EndsWith(Environment.NewLine);
// ASSERT
Assert.IsTrue(endsWithNewLine);
}
#endregion
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment