/// <summary>
/// Takes a List of type <typeparamref name="T"/> and a function as the key selector
/// and returns a unique list of <typeparamref name="T"/>.
/// </summary>
/// <typeparam name="KEY">A class instance that uniquely identifies the current item. Strings work well.</typeparam>
/// <typeparam name="T">The type of items in the target and return lists.</typeparam>
/// <param name="pList">The input list. Not changed by this method.</param>
/// <param name="pGetUniqueKeyFunc">The function that returns a KEY that uniquely identifies the current item</param>
/// <returns>A list of <typeparamref name="T"/> where every item is unique by KEY</returns>
/// <example>
/// <code>List<MyClass> aDistinctList = aTargetList.Distinct( i => i.Id);</code>
/// </example>
/// <remarks>
/// <para>
/// Worth noting that there is a distinct method on the IQueryable<T>, but this does not work
/// on List<T>. This is an entirely different implementation form that method.
/// </para>
/// <para>
/// The order of the original list is maintained, only duplicats are removed.
/// </para>
/// </remarks>
public static List<T> Distinct<KEY, T>(this List<T> pList, Func<T, KEY> pGetUniqueKeyFunc)
{
if (pGetUniqueKeyFunc == null)
throw new ArgumentNullException("pGetUniqueKeyFunc");
if (pList == null pList.Count == 0)
return new List<T>(0);
var aDistinctKeyHash = new Hashtable(); // the list of unique keys - used for lookup
var aDistinctItemList = new List<T>();
// go thru each item in the source list and get the key for that item from the delegate
// if the key does not exist in the hashtable then add it to the hashtable and the item return list
// rinse - repeat...
foreach (var aItem in pList)
{
var aKey = pGetUniqueKeyFunc.Invoke(aItem);
if (!aDistinctKeyHash.ContainsKey(aKey))
{
aDistinctKeyHash.Add(aKey, aItem);
aDistinctItemList.Add(aItem);
}
}
return aDistinctItemList;
}
1 comments:
In my post I imply that there is not a distinct for IEnumerable - when in fact there is. There are two overloaded methods in the framework; one does not take any params and the other takes an IEqualityComparer. I assume that they work basically the same way. Personally, I like the way most of the other extension methods are where they take a delegate/lambda. By doing so, I don't have to plan ahead by impl the interface or taking the default impl. I like the flexibility of wiring everything up later.
Post a Comment