I was working on some code at work that was using the 2.0 framework and COM interop, and in the course of adding enumerator capability to the old COM code, I worked up a quick Lists.cs static class that does some useful things on IEnumerable<T> classes.
The methods are heavily inspired from the MooTools Array API
/// <summary>
/// Static helper functions for dealing with enumerable items.
/// The design of these helper methods was inspired in part by the mootools array library.
/// </summary>
public static class Lists {
/// <summary>
/// Calls a the provided function for each element in the list.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="list">The list.</param>
/// <param name="func">The func.</param>
public static void Each<T>(IEnumerable<T> list, Action<T> func) {
foreach (T item in list) {
func(item);
}
}
/// <summary>
/// Whiles the specified list.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="list">The list.</param>
/// <param name="condition">The condition.</param>
/// <param name="func">The func.</param>
public static void While<T>(IEnumerable<T> list, Predicate<T> condition, Action<T> func) {
foreach (T item in list) {
if (!condition(item))
return;
func(item);
}
}
/// <summary>
/// Returns true if every element in the list satisfies the provided testing function.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="list">The list.</param>
/// <param name="func">The func.</param>
/// <returns></returns>
public static bool Every<T>(IEnumerable<T> list, Predicate<T> func) {
bool called = false;
foreach (T item in list) {
called = true;
if (!func(item)) {
return false;
}
}
return called;
}
/// <summary>
/// Creates a new list with all of the elements of the list for which the provided filtering function returns true.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="list">The list.</param>
/// <param name="func">The func.</param>
/// <returns></returns>
public static IEnumerable<T> Filter<T>(IEnumerable<T> list, Predicate<T> func) {
foreach (T item in list) {
if (func(item)) {
yield return item;
}
}
}
/// <summary>
/// Creates a new list with all of the elements of the source list which are not the default for the list item type.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="list">The list.</param>
/// <returns></returns>
public static IEnumerable<T> Clean<T>(IEnumerable<T> list) {
foreach (T item in list) {
if (!Equals(item, default(T))) {
yield return item;
}
}
}
/// <summary>
/// Returns a list with the results of calling a provided function on every element in the source list.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="list">The list.</param>
/// <param name="func">The func.</param>
/// <returns></returns>
public static IEnumerable<T> Map<T>(IEnumerable<T> list, Converter<T, T> func) {
foreach (T item in list) {
yield return func(item);
}
}
/// <summary>
/// Returns a list with the results of calling a provided function on every element in the source list.
/// </summary>
/// <typeparam name="TInput">The type of the input.</typeparam>
/// <typeparam name="TOutput">The type of the output.</typeparam>
/// <param name="list">The list.</param>
/// <param name="func">The converter function.</param>
/// <returns></returns>
public static IEnumerable<TOutput> Convert<TInput, TOutput>(IEnumerable<TInput> list, Converter<TInput, TOutput> func) {
foreach (TInput item in list) {
yield return func(item);
}
}
/// <summary>
/// Returns true if at least one element in the source list satisfies the provided testing function.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="list">The list.</param>
/// <param name="func">The func.</param>
/// <returns></returns>
public static bool Some<T>(IEnumerable<T> list, Predicate<T> func) {
foreach (T item in list) {
if (func(item)) {
return true;
}
}
return false;
}
/// <summary>
/// Returns a list of key-value pairs based on the list of keys passed in and the items in the source list.
/// </summary>
/// <typeparam name="K">The Key type</typeparam>
/// <typeparam name="V">The Value type</typeparam>
/// <param name="keys">The list of keys.</param>
/// <param name="list">The list of values.</param>
/// <returns></returns>
public static IEnumerable<KeyValuePair<K, V>> Link<K, V>(IEnumerable<K> keys, IEnumerable<V> list) {
using (var keyIterator = keys.GetEnumerator()) {
using (var valueIterator = list.GetEnumerator()) {
while (keyIterator.MoveNext() &&
valueIterator.MoveNext()) {
yield return new KeyValuePair<K, V>(keyIterator.Current, valueIterator.Current);
}
}
}
}
/// <summary>
/// Tests the list for the presence of an item.
/// </summary>
/// <typeparam name="T">The item types in the list, this type must be equatable to itself</typeparam>
/// <param name="list">The list.</param>
/// <param name="item">The item.</param>
/// <returns>
/// <c>true</c> if the specified list contains the item; otherwise, <c>false</c>.
/// </returns>
public static bool Contains<T>(IEnumerable<T> list, T item) where T : IEquatable<T> {
foreach (T val in list) {
if (Equals(val, item)) {
return true;
}
}
return false;
}
/// <summary>
/// Extends a list with all the items of another list.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="list">The list.</param>
/// <param name="additions">The additions.</param>
/// <returns></returns>
public static IEnumerable<T> Extend<T>(IEnumerable<T> list, params IEnumerable<T>[] additions) {
foreach (T item in list) {
yield return item;
}
foreach (IEnumerable<T> exList in additions) {
foreach (T item in exList) {
yield return item;
}
}
}
}
And here is some of the unit test code I did to explain some of the methods if they aren't clear:
[TestFixture]
public class ListsTests {
public string[] FirstNames;
public string[] LastNames;
[TestFixtureSetUp]
public void Init() {
FirstNames = new[] {"jake", "chris", "jason", "bass", "brian", "russ"};
LastNames = new[] {"heidt", "oliver", "torres", "bassalino", "dorry", "guzetta"};
}
[Test]
public void Clean() {
string[] clean_list = new[] {null, null, "some", "strings up in this", null};
int itemsReturned = 0;
foreach (string x in Lists.Clean(clean_list)) {
Assert.IsNotNull(x);
itemsReturned++;
}
Assert.IsTrue(itemsReturned == 2);
}
[Test]
public void Contains() {
Assert.IsTrue(Lists.Contains(FirstNames, "jake"));
Assert.IsTrue(Lists.Contains(FirstNames, "chris"));
Assert.IsTrue(Lists.Contains(FirstNames, "jason"));
Assert.IsTrue(Lists.Contains(FirstNames, "bass"));
Assert.IsTrue(Lists.Contains(FirstNames, "brian"));
Assert.IsTrue(Lists.Contains(FirstNames, "russ"));
Assert.IsFalse(Lists.Contains(FirstNames, "RUSS"));
}
[Test]
public void Each() {
int firstNamesWithEvenLetters = 0;
Lists.Each(FirstNames, delegate(string x) {
if (x.Length % 2 == 0) {
firstNamesWithEvenLetters++;
}
});
Assert.IsTrue(firstNamesWithEvenLetters == 3); // jake, bass, russ
}
[Test]
public void Every() {
/* test that each name contains a vowel, using Lists.Every */
Assert.IsTrue(
Lists.Every(FirstNames, delegate(string x) { return x.IndexOfAny("aeiou".ToCharArray()) > 0; // contains a vowel
})
);
}
[Test]
public void Extend() {
int targetCount = 0;
int extendCount = 0;
IEnumerable<string> fNames = FirstNames;
IEnumerable<string> lNames = LastNames;
foreach (string name in fNames) {
targetCount++;
}
foreach (string name in lNames) {
targetCount++;
}
IEnumerable<string> bothNameParts = Lists.Extend(fNames, lNames);
foreach (string name in bothNameParts) {
extendCount++;
}
Assert.IsTrue(targetCount == extendCount);
}
[Test]
public void Filter() {
IEnumerable<string> evenNames = Lists.Filter(FirstNames, delegate(string x) { return x != null && x.Length % 2 == 0; });
foreach (string name in evenNames) {
Trace.WriteLine(string.Format(".Filter -> name: {0}", name));
Assert.IsNotNull(name);
Assert.IsTrue(name.Length % 2 == 0);
}
}
[Test]
public void Link() {
IEnumerable<KeyValuePair<string, string>> firstLastNameDict = Lists.Link(FirstNames, LastNames);
foreach (KeyValuePair<string, string> pair in firstLastNameDict) {
Assert.IsNotNull(pair);
Assert.IsNotNull(pair.Key);
Assert.IsNotNull(pair.Value);
if (pair.Key == "jake") {
Assert.IsTrue(pair.Value == "heidt");
}
if (pair.Key == "russ") {
Assert.IsTrue(pair.Value == "guzetta");
}
}
}
[Test]
public void Map() {
// convert/format each item in the source list to an upper case string.
IEnumerable<string> upperCaseName = Lists.Map(FirstNames,
delegate(string x) { return x.ToUpper(); }
);
Regex rxUpperCaseOnly = new Regex("^[A-Z ]+$"); // regex test for upper case letters and spaces only
foreach (string name in upperCaseName) {
Assert.IsNotNull(name);
Assert.IsTrue(rxUpperCaseOnly.IsMatch(name));
}
}
[Test]
public void Some() {
Assert.IsTrue(Lists.Some(FirstNames, delegate(string x) { return x.Contains("eidt"); }));
}
[Test]
public void Example() {
}
}