Extension Methods Roundup: Remove, Aggregate, At, AsIndexed and Friends
Hey hey hey! It's time for another Extension Methods Roundup! Here are some of the extension methods I've written since the last one:
Dictionary's Missing Remove Methods
public static void Remove<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TValue value)
{
// Check to see that dictionary is not null
if (dictionary == null)
throw new ArgumentNullException("dictionary");
foreach (var key in (from pair in dictionary
where EqualityComparer<TValue>.Default.Equals(value, pair.Value)
select pair.Key).ToArray())
{
dictionary.Remove(key);
}
}
public static void RemoveRange<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, IEnumerable<TValue> values)
{
// Check to see that dictionary is not null
if (dictionary == null)
throw new ArgumentNullException("dictionary");
// Check to see that values is not null
if (values == null)
throw new ArgumentNullException("values");
foreach (var value in values.ToArray())
{
ExtensionMethods.Remove(dictionary, value);
}
}
public static void RemoveRange<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, IEnumerable<TKey> keys)
{
// Check to see that dictionary is not null
if (dictionary == null)
throw new ArgumentNullException("dictionary");
// Check to see that keys is not null
if (keys == null)
throw new ArgumentNullException("keys");
foreach (var key in keys.ToArray())
{
dictionary.Remove(key);
}
}
String Aggregation
public static string Aggregate(this IEnumerable<string> enumeration, string separator)
{
return Aggregate(enumeration, str => str, separator);
}
public static string Aggregate<T>(this IEnumerable<T> enumeration, Func<T, string> toString, string separator)
{
// Check to see that enumeration is not null
if (enumeration == null)
throw new ArgumentNullException("enumeration");
// Check to see that toString is not null
if (toString == null)
throw new ArgumentNullException("toString");
// Check to see that separator is not null or an empty string
if (string.IsNullOrEmpty(separator))
throw new ArgumentNullException("separator");
return enumeration.Aggregate(string.Empty,
(accum, item) => string.Format("{0}{1}{2}", accum, separator, toString(item)),
str => str.Length > separator.Length ? str.Substring(separator.Length) : str);
}
Those are very good for when you want to create strings such as "a, b, c, d".
LastOrDefault
public static T LastOrDefault<T>(this IList<T> list)
{
// Check to see that list is not null
if (list == null)
throw new ArgumentNullException("list");
if (list.Count == 0)
return default(T);
return list[list.Count - 1];
}
This is an optimized version of the original LastOrDefault for lists that allow random access.
At
public static T At<T>(this IEnumerable<T> enumeration, int index)
{
// Check to see that enumeration is not null
if (enumeration == null)
throw new ArgumentNullException("enumeration");
return enumeration.Skip(index).First();
}
public static IEnumerable<T> At<T>(this IEnumerable<T> enumeration, params int[] indices)
{
return At(enumeration, (IEnumerable<int>)indices);
}
public static IEnumerable<T> At<T>(this IEnumerable<T> enumeration, IEnumerable<int> indices)
{
// Check to see that enumeration is not null
if (enumeration == null)
throw new ArgumentNullException("enumeration");
// Check to see that indices is not null
if (indices == null)
throw new ArgumentNullException("indices");
int currentIndex = 0;
foreach (int index in indices.OrderBy(i => i))
{
while (currentIndex != index)
{
enumeration = enumeration.Skip(1);
currentIndex++;
}
yield return enumeration.First();
}
}
At provides pseudo-random access to enumerable lists, where needed. I've found use for it in a couple of places which returned indices for non-IList<T> enumerations.
SequenceEqual<T1, T2>
public static bool SequenceEqual<T1, T2>(this IEnumerable<T1> left, IEnumerable<T2> right, Func<T1, T2, bool> comparer)
{
using (IEnumerator<T1> leftE = left.GetEnumerator())
{
using (IEnumerator<T2> rightE = right.GetEnumerator())
{
bool leftNext = leftE.MoveNext(), rightNext = rightE.MoveNext();
while (leftNext && rightNext)
{
// If one of the items isn't the same...
if (!comparer(leftE.Current, rightE.Current))
return false;
leftNext = leftE.MoveNext();
rightNext = rightE.MoveNext();
}
// If left or right is longer
if (leftNext || rightNext)
return false;
}
}
return true;
}
This differs from the original SequenceEqual in that it is able to accept two different types of sequences.
AsIndexed
public static IEnumerable<KeyValuePair<int, T>> AsIndexed<T>(this IEnumerable<T> enumeration)
{
// Check to see that enumeration is not null
if (enumeration == null)
throw new ArgumentNullException("enumeration");
int i = 0;
foreach (var item in enumeration)
{
yield return new KeyValuePair<int, T>(i++, item);
}
}
This is when you need indices, but don't want the overhead of creating an Array<T>.
The Missing SelectMany
public static IEnumerable<T> SelectMany<T>(this IEnumerable<IEnumerable<T>> source)
{
// Check to see that source is not null
if (source == null)
throw new ArgumentNullException("source");
foreach (var enumeration in source)
{
foreach (var item in enumeration)
{
yield return item;
}
}
}
Oh, come on! Why wasn't there a parameterless SelectMany in the framework? Oh well, here's one.
ToDictionary of IGrouping
public static Dictionary<TKey, IEnumerable<TElement>> ToDictionary<TKey, TElement>(
this IEnumerable<IGrouping<TKey, TElement>> enumeration)
{
// Check to see that enumeration is not null
if (enumeration == null)
throw new ArgumentNullException("enumeration");
return enumeration.ToDictionary(item => item.Key, item => item.Cast<TElement>());
}
This is shorthand for when you want to create a dictionary from the result of GroupBy.