//****************************************************************************************************** // CollectionExtensions.cs - Gbtc // // Copyright © 2012, Grid Protection Alliance. All Rights Reserved. // // Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See // the NOTICE file distributed with this work for additional information regarding copyright ownership. // The GPA licenses this file to you under the MIT License (MIT), the "License"; you may // not use this file except in compliance with the License. You may obtain a copy of the License at: // // http://www.opensource.org/licenses/MIT // // Unless agreed to in writing, the subject software distributed under the License is distributed on an // "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the // License for the specific language governing permissions and limitations. // // Code Modification History: // ---------------------------------------------------------------------------------------------------- // 02/23/2003 - J. Ritchie Carroll // Generated original version of source code. // 01/23/2005 - J. Ritchie Carroll // Migrated 2.0 version of source code from 1.1 source (GSF.Shared.Common). // 08/17/2007 - Darrell Zuercher // Edited code comments. // 09/11/2008 - J. Ritchie Carroll // Converted to C# extension functions. // 02/17/2009 - Josh L. Patterson // Edited Code Comments. // 02/20/2009 - J. Ritchie Carroll // Added predicate based IndexOf that extends IList. // 04/02/2009 - J. Ritchie Carroll // Added seed based scramble and unscramble IList extensions. // 06/05/2009 - Pinal C. Patel // Added generic AddRange() extension method for IList. // 06/09/2009 - Pinal C. Patel // Added generic GetRange() extension method for IList. // 08/05/2009 - Josh L. Patterson // Update comments. // 09/14/2009 - Stephen C. Wills // Added new header and license agreement. // 11/17/2009 - Pinal C. Patel // Added generic UpdateRange() extension method for IList. // 03/31/2009 - J. Ritchie Carroll // Added Majority() and Minority() extension methods to IEnumerable. // 03/18/2011 - J. Ritchie Carroll // Added dictionary Merge() extensions method for IDictionary and Any/All extensions for BitArray. // 03/22/2011 - J. Ritchie Carroll // Modified array copy extension to handle zero length (i.e., empty) source arrays. // 10/17/2011 - J. Ritchie Carroll // Added the ability to specify a default value for Majority and Minority when none exists. // 12/13/2012 - Starlynn Danyelle Gilliam // Modified Header. // 03/13/2016 - J. Ritchie Carroll // Added ToPagedList extension for enumerations. // //****************************************************************************************************** using System; using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.Linq; using System.Text; using Random = GSF.Security.Cryptography.Random; namespace GSF.Collections { /// /// Defines extension functions related to manipulation of arrays and collections. /// public static class CollectionExtensions { /// /// Gets a to paginate enumeration for /// a given and specified . /// /// Type of to paginate. /// Source enumeration to paginate. /// Page number (1-based). /// Page size. /// Set of items on a given for specified . public static PagedList ToPagedList(this IEnumerable source, int page, int pageSize) { return new PagedList(source, page, pageSize); } /// /// Merges elements of multiple dictionaries into a single dictionary with no duplicate key values overwritten. /// /// Type of to merge. /// Type of keys. /// Type of values. /// Source dictionary to merge with another dictionary. /// Other dictionaries to merge with source dictionary. /// /// A merged collection of all unique dictionary elements, all merged left to the source with no duplicate /// key values overwritten (i.e., first encountered key value pair is the one that remains in the returned merged dictionary). /// public static T Merge(this T source, params IDictionary[] others) where T : IDictionary, new() { return source.Merge(false, others); } /// /// Merges elements of multiple dictionaries into a single dictionary. /// /// Type of to merge. /// Type of keys. /// Type of values. /// Source dictionary to merge with another dictionary. /// Set to true to overwrite duplicate key values as they are encountered. /// Other dictionaries to merge with source dictionary. /// A merged collection of all unique dictionary elements, all merged left to the source. public static T Merge(this T source, bool overwriteExisting, params IDictionary[] others) where T : IDictionary, new() { T mergedDictionary = new T(); List> allDictionaries = new List>(); allDictionaries.Add(source); allDictionaries.AddRange(others); foreach (IDictionary dictionary in allDictionaries) { foreach (KeyValuePair kvPair in dictionary) { if (overwriteExisting) { mergedDictionary[kvPair.Key] = kvPair.Value; } else { if (!mergedDictionary.ContainsKey(kvPair.Key)) mergedDictionary.Add(kvPair); } } } return mergedDictionary; } /// /// Attempts to get the value for the given key and returns the default value instead if the key does not exist in the . /// /// The type of the keys in the dictionary. /// The type of the values in the dictionary. /// The dictionary to check for the given key. /// The key to be checked for the existence of a value. /// The value of the key in the dictionary or the default value if no such value exists. public static TValue GetOrDefault(this IDictionary dictionary, TKey key) { return dictionary.GetOrDefault(key, k => default(TValue)); } /// /// Attempts to get the value for the given key and returns the default value instead if the key does not exist in the . /// /// The type of the keys in the dictionary. /// The type of the values in the dictionary. /// The dictionary to check for the given key. /// The key to be checked for the existence of a value. /// The function used to generate the default value if no value exists for the given key. /// The value of the key in the dictionary or the default value if no such value exists. public static TValue GetOrDefault(this IDictionary dictionary, TKey key, Func defaultValueFactory) { TValue value; if (!dictionary.TryGetValue(key, out value)) value = defaultValueFactory(key); return value; } /// /// Adds a key/value pair to the if the key does not already exist. /// /// The type of the keys in the dictionary. /// The type of the values in the dictionary. /// The dictionary to add the key/value pair to if the key does not already exist. /// The key to be added to the dictionary if it does not already exist. /// The function used to generate a value for the key. /// The value of the key in the dictionary. public static TValue GetOrAdd(this IDictionary dictionary, TKey key, Func valueFactory) { TValue value; if (!dictionary.TryGetValue(key, out value)) { value = valueFactory(key); dictionary.Add(key, value); } return value; } /// /// Adds a key/value pair to the if the key does not already exist. /// /// The type of the keys in the dictionary. /// The type of the values in the dictionary. /// The dictionary to add the key/value pair to if the key does not already exist. /// The key to be added to the dictionary if it does not already exist. /// The value to assign to the key if the key does not already exist. /// The value of the key in the dictionary. public static TValue GetOrAdd(this IDictionary dictionary, TKey key, TValue value) { TValue tryGetValue; if (!dictionary.TryGetValue(key, out tryGetValue)) { tryGetValue = value; dictionary.Add(key, tryGetValue); } return tryGetValue; } /// /// Adds a key/value pair to the if the key does not already exist, /// or updates a key/value pair in the if the key already exists. /// /// The dictionary to add the key/value pair to if the key does not already exist. /// The key to be added or whose value should be updated /// The function used to generate a value for an absent key /// The function used to generate a new value for an existing key based on the key's existing value /// The new value for the key. This will be either be the result of addValueFactory (if the key was absent) or the result of updateValueFactory (if the key was present). public static TValue AddOrUpdate(this IDictionary dictionary, TKey key, Func addValueFactory, Func updateValueFactory) { TValue value; if (dictionary.TryGetValue(key, out value)) value = updateValueFactory(key, value); else value = addValueFactory(key); dictionary[key] = value; return value; } /// /// Adds a key/value pair to the if the key does not already exist, /// or updates a key/value pair in the if the key already exists. /// /// The dictionary to add the key/value pair to if the key does not already exist. /// The key to be added or whose value should be updated /// The value to be added for an absent key /// The function used to generate a new value for an existing key based on the key's existing value /// The new value for the key. This will be either be the result of addValueFactory (if the key was absent) or the result of updateValueFactory (if the key was present). public static TValue AddOrUpdate(this IDictionary dictionary, TKey key, TValue addValue, Func updateValueFactory) { TValue value; if (dictionary.TryGetValue(key, out value)) value = updateValueFactory(key, value); else value = addValue; dictionary[key] = value; return value; } /// /// Adds a key/value pair to the if the key does not already exist, /// or updates a key/value pair in the if the key already exists. /// /// The type of the keys in the dictionary. /// The type of the values in the dictionary. /// The dictionary to add the key/value pair to if the key does not already exist. /// The key to be added or updated. /// The function used to generate a value for the key. /// The value of the key in the dictionary after updating. public static TValue AddOrUpdate(this IDictionary dictionary, TKey key, Func valueFactory) { TValue value = valueFactory(key); dictionary[key] = value; return value; } /// /// Adds a key/value pair to the if the key does not already exist, /// or updates a key/value pair in the if the key already exists. /// /// The type of the keys in the dictionary. /// The type of the values in the dictionary. /// The dictionary to add the key/value pair to if the key does not already exist. /// The key to be added or updated. /// The value to be assigned to the key. /// The value of the key in the dictionary after updating. public static TValue AddOrUpdate(this IDictionary dictionary, TKey key, TValue value) { dictionary[key] = value; return value; } /// /// Converts a name/value collection to a dictionary. /// /// Name/value collection. /// Dictionary converted from a name/value collection. public static Dictionary ToDictionary(this NameValueCollection collection) { return collection.AllKeys.ToDictionary(key => key, key => collection[key]); } /// /// Returns true if any item in is equal to . /// /// Source . /// value to test for. /// true if any item in is equal to . public static bool Any(this BitArray source, bool value) { return source.Cast().Any(item => item == value); } /// /// Returns true if all items in are equal to . /// /// Source . /// value to test for. /// true if all items in are equal to . public static bool All(this BitArray source, bool value) { return source.Cast().All(item => item == value); } /// /// Returns the majority value in the collection, or default type value if no item represents the majority. /// /// of elements in the . /// An enumeration over which to find the majority element. /// The majority value in the collection. public static T Majority(this IEnumerable source) { return source.Majority(default(T)); } /// /// Returns the majority value in the collection, or if no item represents the majority. /// /// of elements in the . /// An enumeration over which to find the majority element. /// Default value to return if no item represents the majority. /// The majority value in the collection. public static T Majority(this IEnumerable source, T defaultValue) { T majority = defaultValue; if ((object)source == null) return majority; if (source.Count() > 1) { Dictionary itemCounts = new Dictionary(); int count; // Count each number of items in the list foreach (T item in source) { if (itemCounts.TryGetValue(item, out count)) { count++; itemCounts[item] = count; } else { itemCounts.Add(item, 1); } } // Find the largest number of items in the list KeyValuePair maxItem = itemCounts.Max((a, b) => (a.Value < b.Value ? -1 : (a.Value > b.Value ? 1 : 0))); // If item with largest count has a plural majority, then it is the majority item if (maxItem.Value > 1) majority = maxItem.Key; } else { majority = source.FirstOrDefault(); } return majority; } /// /// Returns the minority value in the collection, or default type value if no item represents the minority. /// /// of elements in the . /// An enumeration over which to find the minority element. /// The minority value in the collection. public static T Minority(this IEnumerable source) { return source.Minority(default(T)); } /// /// Returns the minority value in the collection, or if no item represents the minority. /// /// of elements in the . /// An enumeration over which to find the minority element. /// Default value to return if no item represents the minority. /// The minority value in the collection. public static T Minority(this IEnumerable source, T defaultValue) { T minority = defaultValue; if ((object)source == null) return minority; if (source.Count() > 1) { Dictionary itemCounts = new Dictionary(); int count; // Count each number of items in the list foreach (T item in source) { if (itemCounts.TryGetValue(item, out count)) { count++; itemCounts[item] = count; } else { itemCounts.Add(item, 1); } } // Find the smallest number of items in the list KeyValuePair minItem = itemCounts.Min((a, b) => (a.Value < b.Value ? -1 : (a.Value > b.Value ? 1 : 0))); minority = minItem.Key; } else { minority = source.FirstOrDefault(); } return minority; } /// /// Adds the specified to the . /// /// of elements in the . /// The collection to which the are to be added. /// The elements to be added to the . public static void AddRange(this IList collection, IEnumerable items) { foreach (T item in items) { collection.Add(item); } } /// /// Updates starting at the with the specified . /// /// of elements in the . /// The collection whose elements are to be updated with the specified . /// The zero-based index in the at which elements are to be updated. /// The elements that will replace the elements starting at the . /// The specified is present in the . public static void UpdateRange(this IList collection, int index, IEnumerable items) { foreach (T item in items) { collection[index++] = item; } } /// /// Returns elements in the specified range from the . /// /// of elements in the . /// The collection from which elements are to be retrieved. /// The 0-based index position in the from which elements are to be retrieved. /// The number of elements to be retrieved from the starting at the . /// An object. public static IList GetRange(this IList collection, int index, int count) { List result = new List(); for (int i = index; i < index + count; i++) { result.Add(collection[i]); } return result; } /// /// Returns the index of the first element of the sequence that satisfies a condition or -1 if no such element is found. /// /// A to find an index in. /// A function to test each element for a condition. /// Index of the first element in that matches the specified ; otherwise, -1. /// The type of the elements of . public static int IndexOf(this IList source, Func predicate) { for (int index = 0; index < source.Count; index++) { if (predicate(source[index])) return index; } return -1; } /// /// Returns a copy of the . /// /// of the to be copied. /// The source whose elements are to be copied. /// The source array index from where the elements are to be copied. /// The number of elements to be copied starting from the startIndex. /// An of elements copied from the specified portion of the source . /// /// Returned will be extended as needed to make it the specified , but /// it will never be less than the source length - . /// /// /// is outside the range of valid indexes for the source -or- /// is less than 0. /// public static T[] Copy(this T[] source, int startIndex, int length) { if (startIndex < 0 || length < 0) throw new ArgumentOutOfRangeException(startIndex < 0 ? "startIndex" : "length", "Cannot be negative"); if (source.Length == 0) return new T[length]; if (startIndex >= source.Length) throw new ArgumentOutOfRangeException("startIndex", "Not a valid index into source buffer"); // Create a new array that will be returned with the specified array elements. T[] copyOfSource = new T[source.Length - startIndex < length ? source.Length - startIndex : length]; Array.Copy(source, startIndex, copyOfSource, 0, copyOfSource.Length); return copyOfSource; } /// Selects the smallest item from the enumeration. /// The generic type of the objects to be selected from. /// The generic type of the objects to be compared. /// An enumeration that contains the objects to be selected from. /// A delegate that takes an object and produces the key for comparison. /// Returns the smallest item from the enumeration. public static TSource MinBy(this IEnumerable source, Func keySelector) where TKey : IComparable { TSource minItem = default(TSource); TKey minKey, nextKey; IEnumerator enumerator = source.GetEnumerator(); if (enumerator.MoveNext()) { minItem = enumerator.Current; minKey = keySelector(minItem); while (enumerator.MoveNext()) { nextKey = keySelector(enumerator.Current); if (nextKey.CompareTo(minKey) < 0) { minItem = enumerator.Current; minKey = nextKey; } } } return minItem; } /// Returns the smallest item from the enumeration. /// The generic type used. /// An enumeration that is compared against. /// A delegate that takes two generic types to compare, and returns an integer based on the comparison. /// Returns a generic type. public static TSource Min(this IEnumerable source, Func comparer) { TSource minItem = default(TSource); IEnumerator enumerator = source.GetEnumerator(); if (enumerator.MoveNext()) { minItem = enumerator.Current; while (enumerator.MoveNext()) { if (comparer(enumerator.Current, minItem) < 0) minItem = enumerator.Current; } } return minItem; } /// Returns the smallest item from the enumeration. /// The generic type used. /// An enumeration that is compared against. /// A comparer object. /// Returns a generic type. public static TSource Min(this IEnumerable source, IComparer comparer) { return source.Min(comparer.Compare); } /// Selects the largest item from the enumeration. /// The generic type of the objects to be selected from. /// The generic type of the objects to be compared. /// An enumeration that contains the objects to be selected from. /// A delegate that takes an object and produces the key for comparison. /// Returns the largest item from the enumeration. public static TSource MaxBy(this IEnumerable source, Func keySelector) where TKey : IComparable { TSource maxItem = default(TSource); TKey maxKey, nextKey; IEnumerator enumerator = source.GetEnumerator(); if (enumerator.MoveNext()) { maxItem = enumerator.Current; maxKey = keySelector(maxItem); while (enumerator.MoveNext()) { nextKey = keySelector(enumerator.Current); if (nextKey.CompareTo(maxKey) > 0) { maxItem = enumerator.Current; maxKey = nextKey; } } } return maxItem; } /// Returns the largest item from the enumeration. /// The generic type used. /// An enumeration that is compared against. /// A delegate that takes two generic types to compare, and returns an integer based on the comparison. /// Returns a generic type. public static TSource Max(this IEnumerable source, Func comparer) { TSource maxItem = default(TSource); IEnumerator enumerator = source.GetEnumerator(); if (enumerator.MoveNext()) { maxItem = enumerator.Current; while (enumerator.MoveNext()) { if (comparer(enumerator.Current, maxItem) > 0) maxItem = enumerator.Current; } } return maxItem; } /// Returns the largest item from the enumeration. /// The generic type used. /// An enumeration that is compared against. /// A comparer object. /// Returns a generic type. public static TSource Max(this IEnumerable source, IComparer comparer) { return source.Max(comparer.Compare); } /// /// Returns only the elements whose keys are distinct. /// /// The type of the source objects in the collection. /// The type of the keys to be compared. /// The collection of source objects. /// The function used to access the keys of the source objects. /// The elements from whose keys are distinct. public static IEnumerable DistinctBy(this IEnumerable source, Func keySelector) { return source .Select(obj => new DistinctByWrapper(keySelector(obj), obj)) .Distinct() .Select(wrapper => wrapper.Value); } /// Converts an enumeration to a string, using the default delimiter ("|") that can later be /// converted back to a list using LoadDelimitedString. /// The generic type used. /// The source object to be converted into a delimited string. /// Returns a that is result of combining all elements in the list delimited by the '|' character. public static string ToDelimitedString(this IEnumerable source) { return source.ToDelimitedString('|'); } /// Converts an enumeration to a string that can later be converted back to a list using /// LoadDelimitedString. /// The generic type used. /// The source object to be converted into a delimited string. /// The delimiting character used. /// Returns a that is result of combining all elements in the list delimited by . public static string ToDelimitedString(this IEnumerable source, char delimiter) { return ToDelimitedString(source, delimiter); } /// Converts an enumeration to a string that can later be converted back to a list using /// LoadDelimitedString. /// The generic type used. /// The source object to be converted into a delimited string. /// The delimiting used. /// Returns a that is result of combining all elements in the list delimited by . public static string ToDelimitedString(this IEnumerable source, string delimiter) { return ToDelimitedString(source, delimiter); } /// Converts an enumeration to a string that can later be converted back to a list using /// LoadDelimitedString. /// The generic enumeration type used. /// The generic delimiter type used. /// The source object to be converted into a delimited string. /// The delimiter of type TDelimiter used. /// Returns a that is result of combining all elements in the list delimited by . private static string ToDelimitedString(IEnumerable source, TDelimiter delimiter) { if (Common.IsReference(delimiter) && (object)delimiter == null) throw new ArgumentNullException("delimiter", "delimiter cannot be null"); StringBuilder delimitedString = new StringBuilder(); foreach (TSource item in source) { if (delimitedString.Length > 0) delimitedString.Append(delimiter); delimitedString.Append(item); } return delimitedString.ToString(); } /// Appends items parsed from delimited string, created with ToDelimitedString, using the default /// delimiter ("|") into the given list. /// Items that are converted are added to list. The list is not cleared in advance. /// The generic type used. /// The list we are adding items to. /// The delimited string to parse for items. /// Delegate that takes one parameter and converts from string to type TSource. public static void LoadDelimitedString(this IList destination, string delimitedString, Func convertFromString) { destination.LoadDelimitedString(delimitedString, '|', convertFromString); } /// Appends items parsed from delimited string, created with ToDelimitedString, into the given list. /// Items that are converted are added to list. The list is not cleared in advance. /// The generic type used. /// The list we are adding items to. /// The delimited string to parse for items. /// The value to look for in the as the delimiter. /// Delegate that takes one parameter and converts from string to type TSource. public static void LoadDelimitedString(this IList destination, string delimitedString, char delimiter, Func convertFromString) { if ((object)delimitedString == null) throw new ArgumentNullException("delimitedString", "delimitedString cannot be null"); if (destination.IsReadOnly) throw new ArgumentException("Cannot add items to a read only list"); foreach (string item in delimitedString.Split(delimiter)) { destination.Add(convertFromString(item.Trim())); } } /// Appends items parsed from delimited string, created with ToDelimitedString, into the given list. /// Items that are converted are added to list. The list is not cleared in advance. /// The generic type used. /// The list we are adding items to. /// The delimited string to parse for items. /// An array of delimiters to look for in the as the delimiter. /// Delegate that takes a and converts to type TSource. public static void LoadDelimitedString(this IList destination, string delimitedString, string[] delimiters, Func convertFromString) { if ((object)delimiters == null) throw new ArgumentNullException("delimiters", "delimiters cannot be null"); if ((object)delimitedString == null) throw new ArgumentNullException("delimitedString", "delimitedString cannot be null"); if (destination.IsReadOnly) throw new ArgumentException("Cannot add items to a read only list"); foreach (string item in delimitedString.Split(delimiters, StringSplitOptions.None)) { destination.Add(convertFromString(item.Trim())); } } /// /// Rearranges all the elements in the list into a highly-random order. /// /// The generic type of the list. /// The input list of generic types to scramble. /// This function uses a cryptographically strong random number generator to perform the scramble. public static void Scramble(this IList source) { if (source.IsReadOnly && !(source is TSource[])) throw new ArgumentException("Cannot modify items in a read only list"); int x, y; TSource currentItem; // Mixes up the data in random order. for (x = 0; x < source.Count; x++) { // Calls random function from GSF namespace. y = Random.Int32Between(0, source.Count - 1); if (x != y) { // Swaps items currentItem = source[x]; source[x] = source[y]; source[y] = currentItem; } } } /// /// Rearranges all the elements in the list into a repeatable pseudo-random order. /// /// The input list of generic types to scramble. /// A number used to calculate a starting value for the pseudo-random number sequence. /// The generic type of the list. /// This function uses the generator to perform the scramble using a sequence that is repeatable. public static void Scramble(this IList source, int seed) { if (source.IsReadOnly && !(source is TSource[])) throw new ArgumentException("Cannot modify items in a read only list"); System.Random random = new System.Random(seed); int x, y, count = source.Count; TSource currentItem; // Mixes up the data in random order. for (x = 0; x < count; x++) { // Calls random function from System namespace. y = random.Next(count); if (x != y) { // Swaps items currentItem = source[x]; source[x] = source[y]; source[y] = currentItem; } } } /// /// Rearranges all the elements in the list previously scrambled with back into their original order. /// /// The input list of generic types to unscramble. /// The same number used in call to scramble original list. /// The generic type of the list. /// This function uses the generator to perform the unscramble using a sequence that is repeatable. public static void Unscramble(this IList source, int seed) { if (source.IsReadOnly && !(source is TSource[])) throw new ArgumentException("Cannot modify items in a read only list"); System.Random random = new System.Random(seed); List sequence = new List(); int x, y, count = source.Count; TSource currentItem; // Generate original scramble sequence. for (x = 0; x < count; x++) { // Calls random function from System namespace. sequence.Add(random.Next(count)); } // Unmix the data order (traverse same sequence in reverse order). for (x = count - 1; x >= 0; x--) { y = sequence[x]; if (x != y) { // Swaps items currentItem = source[x]; source[x] = source[y]; source[y] = currentItem; } } } /// Compares two arrays. /// The first type array to compare to. /// The second type array to compare against. /// true if order of elements should be considered for equality; otherwise, false. /// An which returns 0 if they are equal, 1 if is larger, or -1 if is larger. /// The generic type of the array. /// Cannot compare multidimensional arrays. public static int CompareTo(this TSource[] array1, TSource[] array2, bool orderIsImportant = true) { return CompareTo(array1, array2, Comparer.Default, orderIsImportant); } /// Compares two arrays. /// The first to compare to. /// The second to compare against. /// An interface that exposes a method to compare the two arrays. /// true if order of elements should be considered for equality; otherwise, false. /// An which returns 0 if they are equal, 1 if is larger, or -1 if is larger. /// This is a default comparer to make arrays comparable. /// Cannot compare multidimensional arrays. private static int CompareTo(this Array array1, Array array2, IComparer comparer, bool orderIsImportant = true) { if ((object)comparer == null) throw new ArgumentNullException("comparer"); if ((object)array1 == null && (object)array2 == null) return 0; if ((object)array1 == null) return -1; if ((object)array2 == null) return 1; if (array1.Rank != 1 || array2.Rank != 1) throw new ArgumentException("Cannot compare multidimensional arrays"); // For arrays that do not have the same number of elements, the array with most elements // is assumed to be larger. if (array1.Length != array2.Length) return array1.Length.CompareTo(array2.Length); if (!orderIsImportant) { array1 = array1.Cast().ToArray(); array2 = array2.Cast().ToArray(); Array.Sort(array1, comparer); Array.Sort(array2, comparer); } int comparison = 0; for (int x = 0; x < array1.Length; x++) { comparison = comparer.Compare(array1.GetValue(x), array2.GetValue(x)); if (comparison != 0) break; } return comparison; } private class DistinctByWrapper : IEquatable> { #region [ Members ] // Fields private readonly TKey m_key; private readonly TValue m_value; #endregion #region [ Constructors ] public DistinctByWrapper(TKey key, TValue value) { m_key = key; m_value = value; } #endregion #region [ Properties ] public TValue Value { get { return m_value; } } #endregion #region [ Methods ] public bool Equals(DistinctByWrapper other) { return Equals(m_key, other.m_key); } public override bool Equals(object obj) { DistinctByWrapper wrapper = obj as DistinctByWrapper; if ((object)wrapper != null) return Equals(wrapper); return false; } public override int GetHashCode() { if ((object)m_key == null) return 0; return m_key.GetHashCode(); } #endregion } } }