additional collection extensions

This commit is contained in:
Ryan Peters 2022-03-07 20:15:38 -05:00
parent 93dce4df90
commit 191aaa20cf

View File

@ -7,11 +7,18 @@ using System.Data;
using System.Linq; using System.Linq;
using System.Linq.Expressions; using System.Linq.Expressions;
using System.Reflection; using System.Reflection;
using System.Web;
namespace BinaryDad.Extensions namespace BinaryDad.Extensions
{ {
public static class CollectionExtensions public static class CollectionExtensions
{ {
/// <summary>
/// Adds an item to the <see cref="ICollection{T}"/>, replacing it if it already exists
/// </summary>
/// <typeparam name="TItem"></typeparam>
/// <param name="items"></param>
/// <param name="item"></param>
public static void AddReplace<TItem, TProperty>(this ICollection<TItem> items, TItem item, Func<TItem, TProperty> matchingProperty) where TItem : class where TProperty : IComparable public static void AddReplace<TItem, TProperty>(this ICollection<TItem> items, TItem item, Func<TItem, TProperty> matchingProperty) where TItem : class where TProperty : IComparable
{ {
var existing = items.FirstOrDefault(i => matchingProperty(i).Equals(matchingProperty(item))); var existing = items.FirstOrDefault(i => matchingProperty(i).Equals(matchingProperty(item)));
@ -39,6 +46,21 @@ namespace BinaryDad.Extensions
} }
} }
/// <summary>
/// Groups a set of items into batches of a given batch size
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="items"></param>
/// <param name="batchSize"></param>
/// <returns></returns>
public static IEnumerable<ICollection<T>> Batch<T>(this IEnumerable<T> items, int batchSize)
{
return items.Select((Item, Index) => new { Item, Index })
.GroupBy(x => x.Index / batchSize)
.Select(x => x.Select(t => t.Item).ToList())
.ToList();
}
/// <summary> /// <summary>
/// Returns a distinct list of elements using the first-matched item on a specific property /// Returns a distinct list of elements using the first-matched item on a specific property
/// </summary> /// </summary>
@ -69,6 +91,16 @@ namespace BinaryDad.Extensions
#region ToDataTable #region ToDataTable
/// <summary>
/// Converts a typed collection into a <see cref="DataTable"/>. This method excludes the column if <see cref="NotMappedAttribute"/> is bound to a property.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="collection"></param>
/// <param name="useColumnAttributeName">Specifies whether the data column should use the name from <see cref="ColumnAttribute"/></param>, if bound to a property.
/// <param name="tableName"></param>
/// <returns></returns>
public static DataTable ToDataTable(this IEnumerable collection, bool useColumnAttributeName = false, string tableName = null) => ToDataTable(collection, null, useColumnAttributeName, tableName);
/// <summary> /// <summary>
/// Converts a typed collection into a <see cref="DataTable"/>. This method excludes the column if <see cref="NotMappedAttribute"/> is bound to a property. /// Converts a typed collection into a <see cref="DataTable"/>. This method excludes the column if <see cref="NotMappedAttribute"/> is bound to a property.
/// </summary> /// </summary>
@ -78,7 +110,19 @@ namespace BinaryDad.Extensions
/// <param name="useColumnAttributeName">Specifies whether the data column should use the name from <see cref="ColumnAttribute"/></param>, if bound to a property. /// <param name="useColumnAttributeName">Specifies whether the data column should use the name from <see cref="ColumnAttribute"/></param>, if bound to a property.
/// <param name="tableName"></param> /// <param name="tableName"></param>
/// <returns></returns> /// <returns></returns>
public static DataTable ToDataTable(this IEnumerable collection, Func<PropertyInfo, string> columnNameModifier, bool useColumnAttributeName = false, string tableName = null) public static DataTable ToDataTable(this IEnumerable collection, Func<PropertyInfo, string> columnNameModifier, bool useColumnAttributeName = false, string tableName = null) => ToDataTable(collection, columnNameModifier, null, useColumnAttributeName, tableName);
/// <summary>
/// Converts a typed collection into a <see cref="DataTable"/>. This method excludes the column if <see cref="NotMappedAttribute"/> is bound to a property.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="collection"></param>
/// <param name="columnNameModifier">If a non-null string is returned, the column name is overridden</param>
/// <param name="columnIncludeModifier">If a condition is supplied, only columns matching the condition will be included</param>
/// <param name="useColumnAttributeName">Specifies whether the data column should use the name from <see cref="ColumnAttribute"/></param>, if bound to a property.
/// <param name="tableName"></param>
/// <returns></returns>
public static DataTable ToDataTable(this IEnumerable collection, Func<PropertyInfo, string> columnNameModifier, Func<PropertyInfo, bool> columnIncludeModifier, bool useColumnAttributeName = false, string tableName = null)
{ {
if (collection == null) if (collection == null)
{ {
@ -116,10 +160,25 @@ namespace BinaryDad.Extensions
var propertyInfo = type var propertyInfo = type
.GetProperties() .GetProperties()
.Where(p => !p.HasCustomAttribute<NotMappedAttribute>()) .Where(p =>
{
// automatically ignore [NotMapped] attribute properties
if (p.HasCustomAttribute<NotMappedAttribute>())
{
return false;
}
// allow for custom column inclusion
if (columnIncludeModifier != null)
{
return columnIncludeModifier.Invoke(p);
}
return true;
})
.Select(p => .Select(p =>
{ {
string columnName = p.Name; var columnName = p.Name;
string columnTypeName = null; string columnTypeName = null;
// set column name to be either the property name // set column name to be either the property name
@ -206,6 +265,13 @@ namespace BinaryDad.Extensions
#region Join #region Join
/// <summary>
/// Serializes a collection of objects (wrapper for <see cref="string.Join{T}(string, IEnumerable{T})"/>)
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="items"></param>
/// <param name="separator"></param>
/// <returns></returns>
public static string Join<T>(this IEnumerable<T> items, string separator = "") where T : IComparable public static string Join<T>(this IEnumerable<T> items, string separator = "") where T : IComparable
{ {
if (items == null) if (items == null)
@ -221,6 +287,15 @@ namespace BinaryDad.Extensions
return string.Join(separator, items.ToArray()); return string.Join(separator, items.ToArray());
} }
/// <summary>
/// Serializes a collection of objects with a custom property selector (wrapper for <see cref="string.Join{T}(string, IEnumerable{T})"/>)
/// </summary>
/// <typeparam name="T"></typeparam>
/// <typeparam name="TSelector"></typeparam>
/// <param name="items"></param>
/// <param name="selector"></param>
/// <param name="separator"></param>
/// <returns></returns>
public static string Join<T, TSelector>(this IEnumerable<T> items, Func<T, TSelector> selector, string separator = "") where TSelector : IComparable => Join(items.Select(selector), separator); public static string Join<T, TSelector>(this IEnumerable<T> items, Func<T, TSelector> selector, string separator = "") where TSelector : IComparable => Join(items.Select(selector), separator);
/// <summary> /// <summary>
@ -537,7 +612,48 @@ namespace BinaryDad.Extensions
} }
} }
public static Dictionary<TKey, TSource> ToDictionary<TSource, TKey>(this IEnumerable<KeyValuePair<TKey, TSource>> source, IEqualityComparer<TKey> comparer = null) => source.ToDictionary(k => k.Key, v => v.Value, comparer); public static Dictionary<TKey, TSource> ToDictionary<TSource, TKey>(this IEnumerable<KeyValuePair<TKey, TSource>> source) => source.ToDictionary(k => k.Key, v => v.Value);
public static Dictionary<TKey, TSource> ToDictionary<TSource, TKey>(this IEnumerable<KeyValuePair<TKey, TSource>> source, IEqualityComparer<TKey> comparer) => source.ToDictionary(k => k.Key, v => v.Value, comparer);
public static void Add<TKey, TValue>(this IDictionary<TKey, TValue> source, KeyValuePair<TKey, TValue> value) => source.Add(value.Key, value.Value);
public static T To<T>(this IDictionary<string, object> source) where T : class, new()
{
var instance = new T();
var properties = typeof(T).GetProperties();
foreach (var property in properties)
{
var propertyName = property.Name;
if (source.ContainsKey(propertyName))
{
property.SetValue(instance, source[propertyName]);
}
}
return instance;
}
/// <summary>
/// Creates a query string using a dictionary
/// </summary>
/// <param name="source"></param>
/// <returns></returns>
public static string ToQueryString<T>(this IEnumerable<KeyValuePair<string, T>> source) where T : IConvertible
{
// allows us to get a HttpValueCollection which provides proper serialiation/encoding
var collection = HttpUtility.ParseQueryString(string.Empty);
foreach (var pair in source)
{
collection.Add(pair.Key, pair.Value?.ToString());
}
return collection.ToString();
}
#region Replace #region Replace