C# extension methods for the collection objects.

using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;

namespace Extensions
{
    internal static class CollectionExtensions
    {
        /// <summary>
        /// Adds the item to the list if it doesn't already exist.
        /// </summary>
        /// <typeparam name="T">The type of the T.</typeparam>
        /// <param name="self">The self.</param>
        /// <param name="item">The item.</param>
        /// <returns></returns>
        public static bool AddIfNew<T>(this IList<T> self, T item)
        {
            if (self.Contains(item))
            {
                return false;
            }

            self.Add(item);

            return true;
        }

        /// <summary>
        /// Adds the item to the the list if does not exist.
        /// </summary>
        /// <typeparam name="TKey">The type of the T key.</typeparam>
        /// <typeparam name="TValue">The type of the T value.</typeparam>
        /// <param name="self">The self.</param>
        /// <param name="key">The key.</param>
        /// <param name="value">The value.</param>
        public static void AddToListValue<TKey, TValue>(this Dictionary<TKey, List<TValue>> self, TKey key, TValue value)
        {
            List<TValue> list;

            if (!self.TryGetValue(key, out list))
            {
                list = new List<TValue>();
                self.Add(key, list);
            }

            list.Add(value);
        }

        /// <summary>
        /// Gets or creates the specified value from the Dictionary.
        /// </summary>
        /// <typeparam name="TKey">The type of the T key.</typeparam>
        /// <typeparam name="TValue">The type of the T value.</typeparam>
        /// <param name="self">The self.</param>
        /// <param name="key">The key.</param>
        /// <param name="valueFactory">The value factory.</param>
        /// <returns></returns>
        public static TValue GetOrCreate<TKey, TValue>(this Dictionary<TKey, TValue> self, TKey key, Func<TValue> valueFactory)
        {
            TValue obj;
            if (!self.TryGetValue(key, out obj))
            {
                obj = valueFactory();
                self.Add(key, obj);
            }

            return obj;
        }

        /// <summary>
        /// Adds the or update.
        /// </summary>
        /// <typeparam name="TKey">The type of the T key.</typeparam>
        /// <typeparam name="TValue">The type of the T value.</typeparam>
        /// <param name="self">The self.</param>
        /// <param name="key">The key.</param>
        /// <param name="value">The value.</param>
        public static void AddOrUpdate<TKey, TValue>(this Dictionary<TKey, TValue> self, TKey key, TValue value)
        {
            if (self.ContainsKey(key))
            {
                self.Remove(key);
            }

            self.Add(key, value);
        }

        /// <summary>
        /// Converts a <see cref="NameValueCollection" /> to a <see cref="IDictionary{TKey,TValue}" /> instance.
        /// </summary>
        /// <param name="self">The <see cref="NameValueCollection" /> to convert.</param>
        /// <returns>An <see cref="IDictionary{TKey,TValue}" /> instance.</returns>
        public static IDictionary<string, IEnumerable<string>> ToDictionary(this NameValueCollection self)
        {
            return self.AllKeys.ToDictionary<string, string, IEnumerable<string>>(key => key, self.GetValues);
        }

        /// <summary>
        /// Converts an <see cref="IDictionary{TKey,TValue}" /> instance to a <see cref="NameValueCollection" /> instance.
        /// </summary>
        /// <param name="self">The <see cref="IDictionary{TKey,TValue}" /> instance to convert.</param>
        /// <returns>A <see cref="NameValueCollection" /> instance.</returns>
        public static NameValueCollection ToNameValueCollection(this IDictionary<string, IEnumerable<string>> self)
        {
            var collection = new NameValueCollection();

            foreach (string key in self.Keys)
            {
                foreach (string value in self[key])
                {
                    collection.Add(key, value);
                }
            }

            return collection;
        }

        /// <summary>
        /// Merges a collection of <see cref="IDictionary{TKey,TValue}" /> instances into a single one.
        /// </summary>
        /// <param name="self">The list of <see cref="IDictionary{TKey,TValue}" /> instances to merge.</param>
        /// <returns>
        /// An <see cref="IDictionary{TKey,TValue}" /> instance containing the keys and values from the other instances.
        /// </returns>
        public static IDictionary<string, string> Merge(this IEnumerable<IDictionary<string, string>> self)
        {
            var output = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);

            foreach (IDictionary<string, string> dictionary in self.Where(d => d != null))
            {
                foreach (KeyValuePair<string, string> kvp in dictionary)
                {
                    if (!output.ContainsKey(kvp.Key))
                    {
                        output.Add(kvp.Key, kvp.Value);
                    }
                }
            }

            return output;
        }

        /// <summary>
        /// Filters a collection based on a provided key selector.
        /// </summary>
        /// <typeparam name="TSource">The type of the collection to filter.</typeparam>
        /// <typeparam name="TKey">The type of the key to filter by.</typeparam>
        /// <param name="self">The collection filter.</param>
        /// <param name="keySelector">The predicate to filter by.</param>
        /// <returns>
        /// A <see cref="IEnumerable{T}" /> instance with the filtered values.
        /// </returns>
        public static IEnumerable<TSource> DistinctBy<TSource, TKey>(this IEnumerable<TSource> self, Func<TSource, TKey> keySelector)
        {
            var knownKeys = new HashSet<TKey>();

            foreach (TSource element in self)
            {
                if (knownKeys.Add(keySelector(element)))
                {
                    yield return element;
                }
            }
        }
    }
}