695 lines · 35.3 KB · C#
RawDownload
//******************************************************************************************************
//  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 Eclipse Public License -v 1.0 (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/eclipse-1.0.php
//
//  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<T>.
//  04/02/2009 - J. Ritchie Carroll
//       Added seed based scramble and unscramble IList<T> extensions.
//  06/05/2009 - Pinal C. Patel
//       Added generic AddRange() extension method for IList<T>.
//  06/09/2009 - Pinal C. Patel
//       Added generic GetRange() extension method for IList<T>.
//  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<T>.
//  03/31/2009 - J. Ritchie Carroll
//       Added Majority() and Minority() extension methods to IEnumerable<T>.
//  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.
//
//******************************************************************************************************

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Random = GSF.Security.Cryptography.Random;

namespace GSF.Collections
{
    /// <summary>
    /// Defines extension functions related to manipulation of arrays and collections.
    /// </summary>
    public static class CollectionExtensions
    {
        /// <summary>
        /// Merges elements of multiple dictionaries into a single dictionary with no duplicate key values overwritten.
        /// </summary>
        /// <typeparam name="T">Type of <see cref="IDictionary{TKey, TValue}"/> to merge.</typeparam>
        /// <typeparam name="TKey">Type of <see cref="IDictionary{TKey, TValue}"/> keys.</typeparam>
        /// <typeparam name="TValue">Type of <see cref="IDictionary{TKey, TValue}"/> values.</typeparam>
        /// <param name="source">Source dictionary to merge with another dictionary.</param>
        /// <param name="others">Other dictionaries to merge with source dictionary.</param>
        /// <returns>
        /// A merged collection of all unique dictionary elements, all <paramref name="others"/> merged left to the source with no duplicate
        /// key values overwritten (i.e., first encounted key value pair is the one that remains in the returned merged dictionary).
        /// </returns>
        public static T Merge<T, TKey, TValue>(this T source, params IDictionary<TKey, TValue>[] others) where T : IDictionary<TKey, TValue>, new()
        {
            return source.Merge(false, others);
        }

        /// <summary>
        /// Merges elements of multiple dictionaries into a single dictionary.
        /// </summary>
        /// <typeparam name="T">Type of <see cref="IDictionary{TKey, TValue}"/> to merge.</typeparam>
        /// <typeparam name="TKey">Type of <see cref="IDictionary{TKey, TValue}"/> keys.</typeparam>
        /// <typeparam name="TValue">Type of <see cref="IDictionary{TKey, TValue}"/> values.</typeparam>
        /// <param name="source">Source dictionary to merge with another dictionary.</param>
        /// <param name="overwriteExisting">Set to <c>true</c> to overwrite duplicate key values as they are encountered.</param>
        /// <param name="others">Other dictionaries to merge with source dictionary.</param>
        /// <returns>A merged collection of all unique dictionary elements, all <paramref name="others"/> merged left to the source.</returns>
        public static T Merge<T, TKey, TValue>(this T source, bool overwriteExisting, params IDictionary<TKey, TValue>[] others) where T : IDictionary<TKey, TValue>, new()
        {
            T mergedDictionary = new T();
            List<IDictionary<TKey, TValue>> allDictionaries = new List<IDictionary<TKey, TValue>>();

            allDictionaries.Add(source);
            allDictionaries.AddRange(others);

            foreach (IDictionary<TKey, TValue> dictionary in allDictionaries)
            {
                foreach (KeyValuePair<TKey, TValue> kvPair in dictionary)
                {
                    if (overwriteExisting)
                    {
                        mergedDictionary[kvPair.Key] = kvPair.Value;
                    }
                    else
                    {
                        if (!mergedDictionary.ContainsKey(kvPair.Key))
                            mergedDictionary.Add(kvPair);
                    }
                }
            }

            return mergedDictionary;
        }

        /// <summary>
        /// Returns <c>true</c> if any item in <see cref="BitArray"/> is equal to <paramref name="value"/>.
        /// </summary>
        /// <param name="source">Source <see cref="BitArray"/>.</param>
        /// <param name="value"><see cref="bool"/> value to test for.</param>
        /// <returns><c>true</c> if any item in <see cref="BitArray"/> is equal to <paramref name="value"/>.</returns>
        public static bool Any(this BitArray source, bool value)
        {
            return source.Cast<bool>().Any(item => item == value);
        }

        /// <summary>
        /// Returns <c>true</c> if all items in <see cref="BitArray"/> are equal to <paramref name="value"/>.
        /// </summary>
        /// <param name="source">Source <see cref="BitArray"/>.</param>
        /// <param name="value"><see cref="bool"/> value to test for.</param>
        /// <returns><c>true</c> if all items in <see cref="BitArray"/> are equal to <paramref name="value"/>.</returns>
        public static bool All(this BitArray source, bool value)
        {
            return source.Cast<bool>().All(item => item == value);
        }

        /// <summary>
        /// Returns the majority value in the collection, or default type value if no item represents the majority.
        /// </summary>
        /// <typeparam name="T"><see cref="Type"/> of elements in the <paramref name="source"/>.</typeparam>
        /// <param name="source">An enumeration over which to find the majority element.</param>
        /// <returns>The majority value in the collection.</returns>
        public static T Majority<T>(this IEnumerable<T> source)
        {
            return source.Majority(default(T));
        }

        /// <summary>
        /// Returns the majority value in the collection, or <paramref name="defaultValue"/> if no item represents the majority.
        /// </summary>
        /// <typeparam name="T"><see cref="Type"/> of elements in the <paramref name="source"/>.</typeparam>
        /// <param name="source">An enumeration over which to find the majority element.</param>
        /// <param name="defaultValue">Default value to return if no item represents the majority.</param>
        /// <returns>The majority value in the collection.</returns>
        public static T Majority<T>(this IEnumerable<T> source, T defaultValue)
        {
            T majority = defaultValue;

            if ((object)source != null && source.Count() > 1)
            {
                Dictionary<T, int> itemCounts = new Dictionary<T, int>();
                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<T, int> 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;
        }

        /// <summary>
        /// Returns the minority value in the collection, or default type value if no item represents the minority.
        /// </summary>
        /// <typeparam name="T"><see cref="Type"/> of elements in the <paramref name="source"/>.</typeparam>
        /// <param name="source">An enumeration over which to find the minority element.</param>
        /// <returns>The minority value in the collection.</returns>
        public static T Minority<T>(this IEnumerable<T> source)
        {
            return source.Minority(default(T));
        }

        /// <summary>
        /// Returns the minority value in the collection, or <paramref name="defaultValue"/> if no item represents the minority.
        /// </summary>
        /// <typeparam name="T"><see cref="Type"/> of elements in the <paramref name="source"/>.</typeparam>
        /// <param name="source">An enumeration over which to find the minority element.</param>
        /// <param name="defaultValue">Default value to return if no item represents the minority.</param>
        /// <returns>The minority value in the collection.</returns>
        public static T Minority<T>(this IEnumerable<T> source, T defaultValue)
        {
            T minority = defaultValue;

            if ((object)source != null && source.Count() > 1)
            {
                Dictionary<T, int> itemCounts = new Dictionary<T, int>();
                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<T, int> 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;
        }

        /// <summary>
        /// Adds the specified <paramref name="items"/> to the <paramref name="collection"/>.
        /// </summary>
        /// <typeparam name="T"><see cref="Type"/> of elements in the <paramref name="collection"/>.</typeparam>
        /// <param name="collection">The collection to which the <paramref name="items"/> are to be added.</param>
        /// <param name="items">The elements to be added to the <paramref name="collection"/>.</param>
        public static void AddRange<T>(this IList<T> collection, IEnumerable<T> items)
        {
            foreach (T item in items)
            {
                collection.Add(item);
            }
        }

        /// <summary>
        /// Updates <paramref name="collection"/> starting at the <paramref name="index"/> with the specified <paramref name="items"/>.
        /// </summary>
        /// <typeparam name="T"><see cref="Type"/> of elements in the <paramref name="collection"/>.</typeparam>
        /// <param name="collection">The collection whose elements are to be updated with the specified <paramref name="items"/>.</param>
        /// <param name="index">The zero-based index in the <paramref name="collection"/> at which elements are to be updated.</param>
        /// <param name="items">The elements that will replace the <paramref name="collection"/> elements starting at the <paramref name="index"/>.</param>
        /// <exception cref="ArgumentOutOfRangeException">The specified <paramref name="index"/> is present in the <paramref name="collection"/>.</exception>
        public static void UpdateRange<T>(this IList<T> collection, int index, IEnumerable<T> items)
        {
            foreach (T item in items)
            {
                collection[index++] = item;
            }
        }

        /// <summary>
        /// Returns elements in the specified range from the <paramref name="collection"/>.
        /// </summary>
        /// <typeparam name="T"><see cref="Type"/> of elements in the <paramref name="collection"/>.</typeparam>
        /// <param name="collection">The collection from which elements are to be retrieved.</param>
        /// <param name="index">The 0-based index position in the <paramref name="collection"/> from which elements are to be retrieved.</param>
        /// <param name="count">The number of elements to be retrieved from the <paramref name="collection"/> starting at the <paramref name="index"/>.</param>
        /// <returns>An <see cref="IList{T}"/> object.</returns>
        public static IList<T> GetRange<T>(this IList<T> collection, int index, int count)
        {
            List<T> result = new List<T>();

            for (int i = index; i < index + count; i++)
            {
                result.Add(collection[i]);
            }

            return result;
        }

        /// <summary>
        /// Returns the index of the first element of the sequence that satisfies a condition or <c>-1</c> if no such element is found.
        /// </summary>
        /// <param name="source">A <see cref="IList{T}"/> to find an index in.</param>
        /// <param name="predicate">A function to test each element for a condition.</param>
        /// <returns>Index of the first element in <paramref name="source"/> that matches the specified <paramref name="predicate"/>; otherwise, <c>-1</c>.</returns>
        /// <typeparam name="T">The type of the elements of <paramref name="source"/>.</typeparam>
        public static int IndexOf<T>(this IList<T> source, Func<T, bool> predicate)
        {
            for (int index = 0; index < source.Count; index++)
            {
                if (predicate(source[index]))
                    return index;
            }

            return -1;
        }

        /// <summary>
        /// Returns a copy of the <see cref="Array"/>.
        /// </summary>
        /// <typeparam name="T"><see cref="Type"/> of the <see cref="Array"/> to be copied.</typeparam>
        /// <param name="source">The source <see cref="Array"/> whose elements are to be copied.</param>
        /// <param name="startIndex">The source array index from where the elements are to be copied.</param>
        /// <param name="length">The number of elements to be copied starting from the startIndex.</param>
        /// <returns>An <see cref="Array"/> of elements copied from the specified portion of the source <see cref="Array"/>.</returns>
        /// <remarks>
        /// Returned <see cref="Array"/> will be extended as needed to make it the specified <paramref name="length"/>, but
        /// it will never be less than the source <see cref="Array"/> length - <paramref name="startIndex"/>.
        /// </remarks>
        /// <exception cref="ArgumentOutOfRangeException">
        /// <paramref name="startIndex"/> is outside the range of valid indexes for the source <see cref="Array"/> -or-
        /// <paramref name="length"/> is less than 0.
        /// </exception>
        public static T[] Copy<T>(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;
        }

        /// <summary>Returns the smallest item from the enumeration.</summary>
        /// <typeparam name="TSource">The generic type used.</typeparam>
        /// <param name="source">An enumeration that is compared against.</param>
        /// <param name="comparer">A delegate that takes two generic types to compare, and returns an integer based on the comparison.</param>
        /// <returns>Returns a generic type.</returns>
        public static TSource Min<TSource>(this IEnumerable<TSource> source, Func<TSource, TSource, int> comparer)
        {
            TSource minItem = default(TSource);

            IEnumerator<TSource> enumerator = source.GetEnumerator();

            if (enumerator.MoveNext())
            {
                minItem = enumerator.Current;
                while (enumerator.MoveNext())
                {
                    if (comparer(enumerator.Current, minItem) < 0)
                        minItem = enumerator.Current;
                }
            }

            return minItem;
        }

        /// <summary>Returns the smallest item from the enumeration.</summary>
        /// <typeparam name="TSource">The generic type used.</typeparam>
        /// <param name="source">An enumeration that is compared against.</param>
        /// <param name="comparer">A comparer object.</param>
        /// <returns>Returns a generic type.</returns>
        public static TSource Min<TSource>(this IEnumerable<TSource> source, IComparer<TSource> comparer)
        {
            return source.Min(comparer.Compare);
        }

        /// <summary>Returns the largest item from the enumeration.</summary>
        /// <typeparam name="TSource">The generic type used.</typeparam>
        /// <param name="source">An enumeration that is compared against.</param>
        /// <param name="comparer">A delegate that takes two generic types to compare, and returns an integer based on the comparison.</param>
        /// <returns>Returns a generic type.</returns>
        public static TSource Max<TSource>(this IEnumerable<TSource> source, Func<TSource, TSource, int> comparer)
        {
            TSource maxItem = default(TSource);

            IEnumerator<TSource> enumerator = source.GetEnumerator();

            if (enumerator.MoveNext())
            {
                maxItem = enumerator.Current;
                while (enumerator.MoveNext())
                {
                    if (comparer(enumerator.Current, maxItem) > 0)
                        maxItem = enumerator.Current;
                }
            }

            return maxItem;
        }

        /// <summary>Returns the largest item from the enumeration.</summary>
        /// <typeparam name="TSource">The generic type used.</typeparam>
        /// <param name="source">An enumeration that is compared against.</param>
        /// <param name="comparer">A comparer object.</param>
        /// <returns>Returns a generic type.</returns>
        public static TSource Max<TSource>(this IEnumerable<TSource> source, IComparer<TSource> comparer)
        {
            return source.Max<TSource>(comparer.Compare);
        }

        /// <summary>Converts an enumeration to a string, using the default delimeter ("|") that can later be
        /// converted back to a list using LoadDelimitedString.</summary>
        /// <typeparam name="TSource">The generic type used.</typeparam>
        /// <param name="source">The source object to be converted into a delimited string.</param>
        /// <returns>Returns a <see cref="String"/> that is result of combining all elements in the list delimited by the '|' character.</returns>
        public static string ToDelimitedString<TSource>(this IEnumerable<TSource> source)
        {
            return source.ToDelimitedString<TSource>('|');
        }

        /// <summary>Converts an enumeration to a string that can later be converted back to a list using
        /// LoadDelimitedString.</summary>
        /// <typeparam name="TSource">The generic type used.</typeparam>
        /// <param name="source">The source object to be converted into a delimited string.</param>
        /// <param name="delimiter">The delimiting character used.</param>
        /// <returns>Returns a <see cref="String"/> that is result of combining all elements in the list delimited by <paramref name="delimiter"/>.</returns>
        public static string ToDelimitedString<TSource>(this IEnumerable<TSource> source, char delimiter)
        {
            return ToDelimitedString<TSource, char>(source, delimiter);
        }

        /// <summary>Converts an enumeration to a string that can later be converted back to a list using
        /// LoadDelimitedString.</summary>
        /// <typeparam name="TSource">The generic type used.</typeparam>
        /// <param name="source">The source object to be converted into a delimited string.</param>
        /// <param name="delimiter">The delimiting <see cref="string"/> used.</param>
        /// <returns>Returns a <see cref="String"/> that is result of combining all elements in the list delimited by <paramref name="delimiter"/>.</returns>
        public static string ToDelimitedString<TSource>(this IEnumerable<TSource> source, string delimiter)
        {
            return ToDelimitedString<TSource, string>(source, delimiter);
        }

        /// <summary>Converts an enumeration to a string that can later be converted back to a list using
        /// LoadDelimitedString.</summary>
        /// <typeparam name="TSource">The generic enumeration type used.</typeparam>
        /// <typeparam name="TDelimiter">The generic delimiter type used.</typeparam>
        /// <param name="source">The source object to be converted into a delimited string.</param>
        /// <param name="delimiter">The delimeter of type TDelimiter used.</param>
        /// <returns>Returns a <see cref="String"/> that is result of combining all elements in the list delimited by <paramref name="delimiter"/>.</returns>
        private static string ToDelimitedString<TSource, TDelimiter>(IEnumerable<TSource> source, TDelimiter delimiter)
        {
            if (Common.IsReference(delimiter) && (object)delimiter == null)
                throw new ArgumentNullException("delimiter", "delimiter cannot be null");

            StringBuilder delimetedString = new StringBuilder();

            foreach (TSource item in source)
            {
                if (delimetedString.Length > 0)
                    delimetedString.Append(delimiter);
                delimetedString.Append(item);
            }

            return delimetedString.ToString();
        }

        /// <summary>Appends items parsed from delimited string, created with ToDelimitedString, using the default
        /// delimeter ("|") into the given list.</summary>
        /// <remarks>Items that are converted are added to list. The list is not cleared in advance.</remarks>
        /// <typeparam name="TSource">The generic type used.</typeparam>
        /// <param name="destination">The list we are adding items to.</param>
        /// <param name="delimitedString">The delimited string to parse for items.</param>
        /// <param name="convertFromString">Delegate that takes one parameter and converts from string to type TSource.</param>
        public static void LoadDelimitedString<TSource>(this IList<TSource> destination, string delimitedString, Func<string, TSource> convertFromString)
        {
            destination.LoadDelimitedString(delimitedString, '|', convertFromString);
        }

        /// <summary>Appends items parsed from delimited string, created with ToDelimitedString, into the given list.</summary>
        /// <remarks>Items that are converted are added to list. The list is not cleared in advance.</remarks>
        /// <typeparam name="TSource">The generic type used.</typeparam>
        /// <param name="destination">The list we are adding items to.</param>
        /// <param name="delimitedString">The delimited string to parse for items.</param>
        /// <param name="delimeter">The <see cref="char"/> value to look for in the <paramref name="delimitedString"/> as the delimiter.</param>
        /// <param name="convertFromString">Delegate that takes one parameter and converts from string to type TSource.</param>
        public static void LoadDelimitedString<TSource>(this IList<TSource> destination, string delimitedString, char delimeter, Func<string, TSource> 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(delimeter))

Lines 1–500 of 695.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t