//******************************************************************************************************// 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<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.// 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.Diagnostics.CodeAnalysis;using System.Linq;using System.Text;using System.Threading.Tasks;using Random = GSF.Security.Cryptography.Random; #pragma warning disable SCS0005 // Weak random generator desired for use cases here namespace GSF.Collections{ /// <summary> /// Defines extension functions related to manipulation of arrays and collections. /// </summary> public static class CollectionExtensions { /// <summary> /// Gets a row of data out of a 2-dimensional array. /// </summary> /// <typeparam name="T">Type of array.</typeparam> /// <param name="source">Source array.</param> /// <param name="rowIndex">Row index, i.e., dimension zero value, to retrieve.</param> /// <returns>Values from specified <paramref name="rowIndex"/>.</returns> [SuppressMessage("Microsoft.Performance", "CA1814:PreferJaggedArraysOverMultidimensional", Justification = "This extension method is targeted for a two dimensional array.")] public static IEnumerable<T> GetRow<T>(this T[,] source, int rowIndex) { if (source == null) throw new ArgumentNullException(nameof(source)); if (rowIndex < 0 || source.GetLength(0) > 1) throw new ArgumentOutOfRangeException(nameof(rowIndex)); int length = source.GetLength(1); for (int i = 0; i < length; i++) yield return source[rowIndex, i]; } /// <summary> /// Gets a column of data out of a 2-dimensional array. /// </summary> /// <typeparam name="T">Type of array.</typeparam> /// <param name="source">Source array.</param> /// <param name="columnIndex">Column index, i.e., dimension one value, to retrieve.</param> /// <returns>Values from specified <paramref name="columnIndex"/>.</returns> [SuppressMessage("Microsoft.Performance", "CA1814:PreferJaggedArraysOverMultidimensional", Justification = "This extension method is targeted for a two dimensional array.")] public static IEnumerable<T> GetColumn<T>(this T[,] source, int columnIndex) { // Because of the way yield methods are rewritten by the compiler (they become lazily evaluated state machines) any // exceptions thrown during the parameters check will happen only when the collection is iterated over. That could // happen far away from the source of the buggy code. Therefore it is recommended to split the method into two: // an outer method handling the validation (no longer lazy) and an inner (lazy) method to handle the iteration. if (source == null) throw new ArgumentNullException(nameof(source)); if (columnIndex < 0 || source.GetLength(1) > 1) throw new ArgumentOutOfRangeException(nameof(columnIndex)); return GetColumnIterator(source, columnIndex); } private static IEnumerable<T> GetColumnIterator<T>(T[,] source, int columnIndex) { int length = source.GetLength(0); for (int i = 0; i < length; i++) yield return source[i, columnIndex]; } /// <summary> /// Gets a <see cref="PagedList{T}"/> to paginate <paramref name="source"/> enumeration for /// a given <paramref name="page"/> and specified <paramref name="pageSize"/>. /// </summary> /// <typeparam name="T">Type of <see cref="IEnumerable{T}"/> to paginate.</typeparam> /// <param name="source">Source enumeration to paginate.</param> /// <param name="page">Page number (1-based).</param> /// <param name="pageSize">Page size.</param> /// <param name="count">Total records in source if known.</param> /// <returns>Set of items on a given <paramref name="page"/> for specified <paramref name="pageSize"/>.</returns> /// <remarks> /// If count is known or can be calculated early, specify the value in the /// <paramref name="count"/> parameter as an optimization to prevent a full /// enumeration on <paramref name="source"/> to get a count. /// </remarks> public static PagedList<T> ToPagedList<T>(this IEnumerable<T> source, int page, int pageSize, int count = -1) { return new PagedList<T>(source, page, pageSize, count); } /// <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 encountered 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> /// Attempts to get the value for the given key and returns the default value instead if the key does not exist in the <see cref="IDictionary{TKey, TValue}"/>. /// </summary> /// <typeparam name="TKey">The type of the keys in the dictionary.</typeparam> /// <typeparam name="TValue">The type of the values in the dictionary.</typeparam> /// <param name="dictionary">The dictionary to check for the given key.</param> /// <param name="key">The key to be checked for the existence of a value.</param> /// <returns>The value of the key in the dictionary or the default value if no such value exists.</returns> public static TValue GetOrDefault<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key) { return dictionary.GetOrDefault(key, k => default(TValue)); } /// <summary> /// Attempts to get the value for the given key and returns the default value instead if the key does not exist in the <see cref="IDictionary{TKey, TValue}"/>. /// </summary> /// <typeparam name="TKey">The type of the keys in the dictionary.</typeparam> /// <typeparam name="TValue">The type of the values in the dictionary.</typeparam> /// <param name="dictionary">The dictionary to check for the given key.</param> /// <param name="key">The key to be checked for the existence of a value.</param> /// <param name="defaultValueFactory">The function used to generate the default value if no value exists for the given key.</param> /// <returns>The value of the key in the dictionary or the default value if no such value exists.</returns> public static TValue GetOrDefault<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key, Func<TKey, TValue> defaultValueFactory) { if (!dictionary.TryGetValue(key, out TValue value)) value = defaultValueFactory(key); return value; } /// <summary> /// Adds a key/value pair to the <see cref="IDictionary{TKey, TValue}"/> if the key does not already exist. /// </summary> /// <typeparam name="TKey">The type of the keys in the dictionary.</typeparam> /// <typeparam name="TValue">The type of the values in the dictionary.</typeparam> /// <param name="dictionary">The dictionary to add the key/value pair to if the key does not already exist.</param> /// <param name="key">The key to be added to the dictionary if it does not already exist.</param> /// <param name="valueFactory">The function used to generate a value for the key.</param> /// <returns>The value of the key in the dictionary.</returns> public static TValue GetOrAdd<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key, Func<TKey, TValue> valueFactory) { if (!dictionary.TryGetValue(key, out TValue value)) { value = valueFactory(key); dictionary.Add(key, value); } return value; } /// <summary> /// Adds a key/value pair to the <see cref="IDictionary{TKey, TValue}"/> if the key does not already exist. /// </summary> /// <typeparam name="TKey">The type of the keys in the dictionary.</typeparam> /// <typeparam name="TValue">The type of the values in the dictionary.</typeparam> /// <param name="dictionary">The dictionary to add the key/value pair to if the key does not already exist.</param> /// <param name="key">The key to be added to the dictionary if it does not already exist.</param> /// <param name="value">The value to assign to the key if the key does not already exist.</param> /// <returns>The value of the key in the dictionary.</returns> public static TValue GetOrAdd<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key, TValue value) { if (!dictionary.TryGetValue(key, out TValue tryGetValue)) { tryGetValue = value; dictionary.Add(key, tryGetValue); } return tryGetValue; } /// <summary> /// Adds a key/value pair to the <see cref="IDictionary{TKey, TValue}"/> if the key does not already exist, /// or updates a key/value pair in the <see cref="IDictionary{TKey, TValue}"/> if the key already exists. /// </summary> /// <param name="dictionary">The dictionary to add the key/value pair to if the key does not already exist.</param> /// <param name="key">The key to be added or whose value should be updated</param> /// <param name="addValueFactory">The function used to generate a value for an absent key</param> /// <param name="updateValueFactory">The function used to generate a new value for an existing key based on the key's existing value</param> /// <returns>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).</returns> public static TValue AddOrUpdate<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key, Func<TKey, TValue> addValueFactory, Func<TKey, TValue, TValue> updateValueFactory) { if (dictionary.TryGetValue(key, out TValue value)) value = updateValueFactory(key, value); else value = addValueFactory(key); dictionary[key] = value; return value; } /// <summary> /// Adds a key/value pair to the <see cref="IDictionary{TKey, TValue}"/> if the key does not already exist, /// or updates a key/value pair in the <see cref="IDictionary{TKey, TValue}"/> if the key already exists. /// </summary> /// <param name="dictionary">The dictionary to add the key/value pair to if the key does not already exist.</param> /// <param name="key">The key to be added or whose value should be updated</param> /// <param name="addValue">The value to be added for an absent key</param> /// <param name="updateValueFactory">The function used to generate a new value for an existing key based on the key's existing value</param> /// <returns>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).</returns> public static TValue AddOrUpdate<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key, TValue addValue, Func<TKey, TValue, TValue> updateValueFactory) { if (dictionary.TryGetValue(key, out TValue value)) value = updateValueFactory(key, value); else value = addValue; dictionary[key] = value; return value; } /// <summary> /// Adds a key/value pair to the <see cref="IDictionary{TKey, TValue}"/> if the key does not already exist, /// or updates a key/value pair in the <see cref="IDictionary{TKey, TValue}"/> if the key already exists. /// </summary> /// <typeparam name="TKey">The type of the keys in the dictionary.</typeparam> /// <typeparam name="TValue">The type of the values in the dictionary.</typeparam> /// <param name="dictionary">The dictionary to add the key/value pair to if the key does not already exist.</param> /// <param name="key">The key to be added or updated.</param> /// <param name="valueFactory">The function used to generate a value for the key.</param> /// <returns>The value of the key in the dictionary after updating.</returns> public static TValue AddOrUpdate<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key, Func<TKey, TValue> valueFactory) { TValue value = valueFactory(key); dictionary[key] = value; return value; } /// <summary> /// Adds a key/value pair to the <see cref="IDictionary{TKey, TValue}"/> if the key does not already exist, /// or updates a key/value pair in the <see cref="IDictionary{TKey, TValue}"/> if the key already exists. /// </summary> /// <typeparam name="TKey">The type of the keys in the dictionary.</typeparam> /// <typeparam name="TValue">The type of the values in the dictionary.</typeparam> /// <param name="dictionary">The dictionary to add the key/value pair to if the key does not already exist.</param> /// <param name="key">The key to be added or updated.</param> /// <param name="value">The value to be assigned to the key.</param> /// <returns>The value of the key in the dictionary after updating.</returns> public static TValue AddOrUpdate<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key, TValue value) { dictionary[key] = value; return value; } /// <summary> /// Converts a name/value collection to a dictionary. /// </summary> /// <param name="collection">Name/value collection.</param> /// <returns>Dictionary converted from a name/value collection.</returns> public static Dictionary<string, string> ToDictionary(this NameValueCollection collection) { return collection.AllKeys.ToDictionary(key => key, key => collection[key]); } /// <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> /// Determines whether all elements of a sequence satisfy a condition with each item being tested in parallel. /// </summary> /// <param name="source">An <see cref="IEnumerable{TSource}" /> that contains the elements to apply the predicate to.</param> /// <param name="predicate">A function to test each element for a condition.</param> /// <param name="options">Any <see cref="ParallelOptions"/> to apply.</param> /// <returns> /// <c>true</c> if every element of the source sequence passes the test in the specified predicate, or if the sequence is empty; otherwise, <c>false</c>. /// </returns> /// <typeparam name="TSource">The type of the elements of <paramref name="source" />.</typeparam> /// <exception cref="ArgumentNullException"><paramref name="source" /> or <paramref name="predicate" /> is <c>null</c>.</exception> public static bool AllParallel<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate, ParallelOptions options = null) { if (source == null) throw new ArgumentNullException(nameof(source)); if (predicate == null) throw new ArgumentNullException(nameof(predicate)); bool succeeded = true; Parallel.ForEach(source, options ?? new ParallelOptions(), (element, state) => { if (!predicate(element)) { succeeded = false; state.Break(); } }); return succeeded; } /// <summary> /// Gets the median item(s) from an enumeration, i.e., one return item for odd lengths, two for even lengths. /// </summary> /// <remarks> /// If enumeration length is even, two items will be returned representing the items in the middle of the set; /// otherwise, if the enumeration length is odd, one item will be returned from exactly the middle of the set. /// </remarks> /// <typeparam name="T"><see cref="Type"/> of elements in the <paramref name="source"/></typeparam> /// <param name="source">An enumeration over which to find the median element(s).</param> /// <returns>The median item(s) from an enumeration, or <c>null</c> if <paramref name="source"/> is <c>null</c>.</returns> public static T[] Median<T>(this IEnumerable<T> source) { if (source == null) return null; T[] values = source as T[] ?? source.ToArray(); if (values.Length < 3) return values; List<T> results = new List<T>(); bool isEven = values.Length % 2 == 0; int midIndex = values.Length / 2; if (isEven) results.Add(values[midIndex - 1]); results.Add(values[midIndex]); return results.ToArray(); } /// <summary> /// Returns the majority value in the collection, or default type value if no item represents the majority. /// </summary> /// <typeparam name="TSource"><see cref="Type"/> of elements in the <paramref name="source"/>.</typeparam> /// <typeparam name="TKey">The generic type of the objects to be compared.</typeparam> /// <param name="source">An enumeration over which to find the majority element.</param> /// <param name="keySelector">A delegate that takes an object and produces the key for comparison.</param> /// <param name="forwardSearch"><c>true</c> to search forward in <paramref name="source"/>; otherwise <c>false</c> to search backwards.</param> /// <param name="comparer">The <see cref="IEqualityComparer{TKey}" /> implementation to use when comparing keys, or <c>null</c> to use the default comparer for the type of the key.</param> /// <returns>The majority value in the collection.</returns> public static TSource MajorityBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, bool forwardSearch = true, IEqualityComparer<TKey> comparer = null) { return source.MajorityBy(default(TSource), keySelector, forwardSearch, comparer); } /// <summary> /// Returns the majority value in the collection, or <paramref name="defaultValue"/> if no item represents the majority. /// </summary> /// <typeparam name="TSource"><see cref="Type"/> of elements in the <paramref name="source"/>.</typeparam> /// <typeparam name="TKey">The generic type of the objects to be compared.</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> /// <param name="keySelector">A delegate that takes an object and produces the key for comparison.</param> /// <param name="forwardSearch"><c>true</c> to search forward in <paramref name="source"/>; otherwise <c>false</c> to search backwards.</param> /// <param name="comparer">The <see cref="IEqualityComparer{TKey}" /> implementation to use when comparing keys, or <c>null</c> to use the default comparer for the type of the key.</param> /// <returns>The majority value in the collection.</returns> public static TSource MajorityBy<TSource, TKey>(this IEnumerable<TSource> source, TSource defaultValue, Func<TSource, TKey> keySelector, bool forwardSearch = true, IEqualityComparer<TKey> comparer = null) { TSource majority = defaultValue; if (source == null) return majority; TSource[] values; if (forwardSearch) values = source as TSource[] ?? source.ToArray(); else values = source.Reverse().ToArray(); if (values.Length > 1) { Dictionary<TKey, Tuple<int, TSource>> itemCounts = new Dictionary<TKey, Tuple<int, TSource>>(comparer); // Count each number of items in the list foreach (TSource value in values) { TKey key = keySelector(value); if (itemCounts.TryGetValue(key, out Tuple<int, TSource> valueCount)) { int count = valueCount.Item1 + 1; itemCounts[key] = new Tuple<int, TSource>(count, valueCount.Item2);Lines 1–500 of 1253. Load more
//****************************************************************************************************** // 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<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. // 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.Diagnostics.CodeAnalysis; using System.Linq; using System.Text; using System.Threading.Tasks; using Random = GSF.Security.Cryptography.Random; #pragma warning disable SCS0005 // Weak random generator desired for use cases here namespace GSF.Collections { /// <summary> /// Defines extension functions related to manipulation of arrays and collections. /// </summary> public static class CollectionExtensions { /// <summary> /// Gets a row of data out of a 2-dimensional array. /// </summary> /// <typeparam name="T">Type of array.</typeparam> /// <param name="source">Source array.</param> /// <param name="rowIndex">Row index, i.e., dimension zero value, to retrieve.</param> /// <returns>Values from specified <paramref name="rowIndex"/>.</returns> [SuppressMessage("Microsoft.Performance", "CA1814:PreferJaggedArraysOverMultidimensional", Justification = "This extension method is targeted for a two dimensional array.")] public static IEnumerable<T> GetRow<T>(this T[,] source, int rowIndex) { if (source == null) throw new ArgumentNullException(nameof(source)); if (rowIndex < 0 || source.GetLength(0) > 1) throw new ArgumentOutOfRangeException(nameof(rowIndex)); int length = source.GetLength(1); for (int i = 0; i < length; i++) yield return source[rowIndex, i]; } /// <summary> /// Gets a column of data out of a 2-dimensional array. /// </summary> /// <typeparam name="T">Type of array.</typeparam> /// <param name="source">Source array.</param> /// <param name="columnIndex">Column index, i.e., dimension one value, to retrieve.</param> /// <returns>Values from specified <paramref name="columnIndex"/>.</returns> [SuppressMessage("Microsoft.Performance", "CA1814:PreferJaggedArraysOverMultidimensional", Justification = "This extension method is targeted for a two dimensional array.")] public static IEnumerable<T> GetColumn<T>(this T[,] source, int columnIndex) { // Because of the way yield methods are rewritten by the compiler (they become lazily evaluated state machines) any // exceptions thrown during the parameters check will happen only when the collection is iterated over. That could // happen far away from the source of the buggy code. Therefore it is recommended to split the method into two: // an outer method handling the validation (no longer lazy) and an inner (lazy) method to handle the iteration. if (source == null) throw new ArgumentNullException(nameof(source)); if (columnIndex < 0 || source.GetLength(1) > 1) throw new ArgumentOutOfRangeException(nameof(columnIndex)); return GetColumnIterator(source, columnIndex); } private static IEnumerable<T> GetColumnIterator<T>(T[,] source, int columnIndex) { int length = source.GetLength(0); for (int i = 0; i < length; i++) yield return source[i, columnIndex]; } /// <summary> /// Gets a <see cref="PagedList{T}"/> to paginate <paramref name="source"/> enumeration for /// a given <paramref name="page"/> and specified <paramref name="pageSize"/>. /// </summary> /// <typeparam name="T">Type of <see cref="IEnumerable{T}"/> to paginate.</typeparam> /// <param name="source">Source enumeration to paginate.</param> /// <param name="page">Page number (1-based).</param> /// <param name="pageSize">Page size.</param> /// <param name="count">Total records in source if known.</param> /// <returns>Set of items on a given <paramref name="page"/> for specified <paramref name="pageSize"/>.</returns> /// <remarks> /// If count is known or can be calculated early, specify the value in the /// <paramref name="count"/> parameter as an optimization to prevent a full /// enumeration on <paramref name="source"/> to get a count. /// </remarks> public static PagedList<T> ToPagedList<T>(this IEnumerable<T> source, int page, int pageSize, int count = -1) { return new PagedList<T>(source, page, pageSize, count); } /// <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 encountered 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> /// Attempts to get the value for the given key and returns the default value instead if the key does not exist in the <see cref="IDictionary{TKey, TValue}"/>. /// </summary> /// <typeparam name="TKey">The type of the keys in the dictionary.</typeparam> /// <typeparam name="TValue">The type of the values in the dictionary.</typeparam> /// <param name="dictionary">The dictionary to check for the given key.</param> /// <param name="key">The key to be checked for the existence of a value.</param> /// <returns>The value of the key in the dictionary or the default value if no such value exists.</returns> public static TValue GetOrDefault<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key) { return dictionary.GetOrDefault(key, k => default(TValue)); } /// <summary> /// Attempts to get the value for the given key and returns the default value instead if the key does not exist in the <see cref="IDictionary{TKey, TValue}"/>. /// </summary> /// <typeparam name="TKey">The type of the keys in the dictionary.</typeparam> /// <typeparam name="TValue">The type of the values in the dictionary.</typeparam> /// <param name="dictionary">The dictionary to check for the given key.</param> /// <param name="key">The key to be checked for the existence of a value.</param> /// <param name="defaultValueFactory">The function used to generate the default value if no value exists for the given key.</param> /// <returns>The value of the key in the dictionary or the default value if no such value exists.</returns> public static TValue GetOrDefault<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key, Func<TKey, TValue> defaultValueFactory) { if (!dictionary.TryGetValue(key, out TValue value)) value = defaultValueFactory(key); return value; } /// <summary> /// Adds a key/value pair to the <see cref="IDictionary{TKey, TValue}"/> if the key does not already exist. /// </summary> /// <typeparam name="TKey">The type of the keys in the dictionary.</typeparam> /// <typeparam name="TValue">The type of the values in the dictionary.</typeparam> /// <param name="dictionary">The dictionary to add the key/value pair to if the key does not already exist.</param> /// <param name="key">The key to be added to the dictionary if it does not already exist.</param> /// <param name="valueFactory">The function used to generate a value for the key.</param> /// <returns>The value of the key in the dictionary.</returns> public static TValue GetOrAdd<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key, Func<TKey, TValue> valueFactory) { if (!dictionary.TryGetValue(key, out TValue value)) { value = valueFactory(key); dictionary.Add(key, value); } return value; } /// <summary> /// Adds a key/value pair to the <see cref="IDictionary{TKey, TValue}"/> if the key does not already exist. /// </summary> /// <typeparam name="TKey">The type of the keys in the dictionary.</typeparam> /// <typeparam name="TValue">The type of the values in the dictionary.</typeparam> /// <param name="dictionary">The dictionary to add the key/value pair to if the key does not already exist.</param> /// <param name="key">The key to be added to the dictionary if it does not already exist.</param> /// <param name="value">The value to assign to the key if the key does not already exist.</param> /// <returns>The value of the key in the dictionary.</returns> public static TValue GetOrAdd<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key, TValue value) { if (!dictionary.TryGetValue(key, out TValue tryGetValue)) { tryGetValue = value; dictionary.Add(key, tryGetValue); } return tryGetValue; } /// <summary> /// Adds a key/value pair to the <see cref="IDictionary{TKey, TValue}"/> if the key does not already exist, /// or updates a key/value pair in the <see cref="IDictionary{TKey, TValue}"/> if the key already exists. /// </summary> /// <param name="dictionary">The dictionary to add the key/value pair to if the key does not already exist.</param> /// <param name="key">The key to be added or whose value should be updated</param> /// <param name="addValueFactory">The function used to generate a value for an absent key</param> /// <param name="updateValueFactory">The function used to generate a new value for an existing key based on the key's existing value</param> /// <returns>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).</returns> public static TValue AddOrUpdate<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key, Func<TKey, TValue> addValueFactory, Func<TKey, TValue, TValue> updateValueFactory) { if (dictionary.TryGetValue(key, out TValue value)) value = updateValueFactory(key, value); else value = addValueFactory(key); dictionary[key] = value; return value; } /// <summary> /// Adds a key/value pair to the <see cref="IDictionary{TKey, TValue}"/> if the key does not already exist, /// or updates a key/value pair in the <see cref="IDictionary{TKey, TValue}"/> if the key already exists. /// </summary> /// <param name="dictionary">The dictionary to add the key/value pair to if the key does not already exist.</param> /// <param name="key">The key to be added or whose value should be updated</param> /// <param name="addValue">The value to be added for an absent key</param> /// <param name="updateValueFactory">The function used to generate a new value for an existing key based on the key's existing value</param> /// <returns>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).</returns> public static TValue AddOrUpdate<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key, TValue addValue, Func<TKey, TValue, TValue> updateValueFactory) { if (dictionary.TryGetValue(key, out TValue value)) value = updateValueFactory(key, value); else value = addValue; dictionary[key] = value; return value; } /// <summary> /// Adds a key/value pair to the <see cref="IDictionary{TKey, TValue}"/> if the key does not already exist, /// or updates a key/value pair in the <see cref="IDictionary{TKey, TValue}"/> if the key already exists. /// </summary> /// <typeparam name="TKey">The type of the keys in the dictionary.</typeparam> /// <typeparam name="TValue">The type of the values in the dictionary.</typeparam> /// <param name="dictionary">The dictionary to add the key/value pair to if the key does not already exist.</param> /// <param name="key">The key to be added or updated.</param> /// <param name="valueFactory">The function used to generate a value for the key.</param> /// <returns>The value of the key in the dictionary after updating.</returns> public static TValue AddOrUpdate<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key, Func<TKey, TValue> valueFactory) { TValue value = valueFactory(key); dictionary[key] = value; return value; } /// <summary> /// Adds a key/value pair to the <see cref="IDictionary{TKey, TValue}"/> if the key does not already exist, /// or updates a key/value pair in the <see cref="IDictionary{TKey, TValue}"/> if the key already exists. /// </summary> /// <typeparam name="TKey">The type of the keys in the dictionary.</typeparam> /// <typeparam name="TValue">The type of the values in the dictionary.</typeparam> /// <param name="dictionary">The dictionary to add the key/value pair to if the key does not already exist.</param> /// <param name="key">The key to be added or updated.</param> /// <param name="value">The value to be assigned to the key.</param> /// <returns>The value of the key in the dictionary after updating.</returns> public static TValue AddOrUpdate<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key, TValue value) { dictionary[key] = value; return value; } /// <summary> /// Converts a name/value collection to a dictionary. /// </summary> /// <param name="collection">Name/value collection.</param> /// <returns>Dictionary converted from a name/value collection.</returns> public static Dictionary<string, string> ToDictionary(this NameValueCollection collection) { return collection.AllKeys.ToDictionary(key => key, key => collection[key]); } /// <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> /// Determines whether all elements of a sequence satisfy a condition with each item being tested in parallel. /// </summary> /// <param name="source">An <see cref="IEnumerable{TSource}" /> that contains the elements to apply the predicate to.</param> /// <param name="predicate">A function to test each element for a condition.</param> /// <param name="options">Any <see cref="ParallelOptions"/> to apply.</param> /// <returns> /// <c>true</c> if every element of the source sequence passes the test in the specified predicate, or if the sequence is empty; otherwise, <c>false</c>. /// </returns> /// <typeparam name="TSource">The type of the elements of <paramref name="source" />.</typeparam> /// <exception cref="ArgumentNullException"><paramref name="source" /> or <paramref name="predicate" /> is <c>null</c>.</exception> public static bool AllParallel<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate, ParallelOptions options = null) { if (source == null) throw new ArgumentNullException(nameof(source)); if (predicate == null) throw new ArgumentNullException(nameof(predicate)); bool succeeded = true; Parallel.ForEach(source, options ?? new ParallelOptions(), (element, state) => { if (!predicate(element)) { succeeded = false; state.Break(); } }); return succeeded; } /// <summary> /// Gets the median item(s) from an enumeration, i.e., one return item for odd lengths, two for even lengths. /// </summary> /// <remarks> /// If enumeration length is even, two items will be returned representing the items in the middle of the set; /// otherwise, if the enumeration length is odd, one item will be returned from exactly the middle of the set. /// </remarks> /// <typeparam name="T"><see cref="Type"/> of elements in the <paramref name="source"/></typeparam> /// <param name="source">An enumeration over which to find the median element(s).</param> /// <returns>The median item(s) from an enumeration, or <c>null</c> if <paramref name="source"/> is <c>null</c>.</returns> public static T[] Median<T>(this IEnumerable<T> source) { if (source == null) return null; T[] values = source as T[] ?? source.ToArray(); if (values.Length < 3) return values; List<T> results = new List<T>(); bool isEven = values.Length % 2 == 0; int midIndex = values.Length / 2; if (isEven) results.Add(values[midIndex - 1]); results.Add(values[midIndex]); return results.ToArray(); } /// <summary> /// Returns the majority value in the collection, or default type value if no item represents the majority. /// </summary> /// <typeparam name="TSource"><see cref="Type"/> of elements in the <paramref name="source"/>.</typeparam> /// <typeparam name="TKey">The generic type of the objects to be compared.</typeparam> /// <param name="source">An enumeration over which to find the majority element.</param> /// <param name="keySelector">A delegate that takes an object and produces the key for comparison.</param> /// <param name="forwardSearch"><c>true</c> to search forward in <paramref name="source"/>; otherwise <c>false</c> to search backwards.</param> /// <param name="comparer">The <see cref="IEqualityComparer{TKey}" /> implementation to use when comparing keys, or <c>null</c> to use the default comparer for the type of the key.</param> /// <returns>The majority value in the collection.</returns> public static TSource MajorityBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, bool forwardSearch = true, IEqualityComparer<TKey> comparer = null) { return source.MajorityBy(default(TSource), keySelector, forwardSearch, comparer); } /// <summary> /// Returns the majority value in the collection, or <paramref name="defaultValue"/> if no item represents the majority. /// </summary> /// <typeparam name="TSource"><see cref="Type"/> of elements in the <paramref name="source"/>.</typeparam> /// <typeparam name="TKey">The generic type of the objects to be compared.</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> /// <param name="keySelector">A delegate that takes an object and produces the key for comparison.</param> /// <param name="forwardSearch"><c>true</c> to search forward in <paramref name="source"/>; otherwise <c>false</c> to search backwards.</param> /// <param name="comparer">The <see cref="IEqualityComparer{TKey}" /> implementation to use when comparing keys, or <c>null</c> to use the default comparer for the type of the key.</param> /// <returns>The majority value in the collection.</returns> public static TSource MajorityBy<TSource, TKey>(this IEnumerable<TSource> source, TSource defaultValue, Func<TSource, TKey> keySelector, bool forwardSearch = true, IEqualityComparer<TKey> comparer = null) { TSource majority = defaultValue; if (source == null) return majority; TSource[] values; if (forwardSearch) values = source as TSource[] ?? source.ToArray(); else values = source.Reverse().ToArray(); if (values.Length > 1) { Dictionary<TKey, Tuple<int, TSource>> itemCounts = new Dictionary<TKey, Tuple<int, TSource>>(comparer); // Count each number of items in the list foreach (TSource value in values) { TKey key = keySelector(value); if (itemCounts.TryGetValue(key, out Tuple<int, TSource> valueCount)) { int count = valueCount.Item1 + 1; itemCounts[key] = new Tuple<int, TSource>(count, valueCount.Item2);