Why Gemfury? Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Debian packages RPM packages NuGet packages

Repository URL to install this package:

Details    
ai.fluctio.fluctio-sim / Utils / Extensions / LinqExtensions.cs
Size: Mime:
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Fluctio.FluctioSim.Utils.General;
using JetBrains.Annotations;

namespace Fluctio.FluctioSim.Utils.Extensions
{
	[SuppressMessage("ReSharper", "MemberCanBePrivate.Global")]
	[SuppressMessage("ReSharper", "UnusedMember.Global")]
	[SuppressMessage("ReSharper", "UnusedMethodReturnValue.Global")]
	public static class LinqExtensions
	{
		[Pure]
		public static LinkedList<T> ToLinkedList<T>(this IEnumerable<T> enumerable)
		{
			return new LinkedList<T>(enumerable);
		}
		
		[Pure]
		public static IEnumerable<T> HideExcess<T>(this ICollection<T> collection, int limit, T lastElement)
		{
			return collection.Count > limit ? collection.Take(limit - 1).Append(lastElement) : collection;
		}

		[Pure]
		public static ProxyLookup<TKey, TMiddleValue, TFinalValue> ToProxyLookup<TKey, TMiddleValue, TFinalValue>(this ILookup<TKey, TMiddleValue> lookup, Func<TKey, IEnumerable<TMiddleValue>, TFinalValue> getter)
		{
			return new ProxyLookup<TKey, TMiddleValue, TFinalValue>(lookup, getter);
		}

		public static void Add<T>(this ICollection<T> collection, T item, bool condition)
		{
			if (condition)
			{
				collection.Add(item);
			}
		}
		
		#region Selectors
		
		[Pure]
		public static Func<IEnumerable<T>, Func<T, bool>, T> GetOneSelector<T>(bool allowZero, bool allowMultiple)
		{
			return (allowZero, allowMultiple) switch
			{
				(false, false) => Enumerable.Single,
				(true, false) => Enumerable.SingleOrDefault,
				(false, true) => Enumerable.First,
				(true, true) => Enumerable.FirstOrDefault,
			};
		}
		
		[Pure]
		public static T One<T>(this IEnumerable<T> claims, Func<T, bool> predicate, bool allowZero = true, bool allowMultiple = true)
		{
			var selector = GetOneSelector<T>(allowZero, allowMultiple);
			return selector(claims, predicate);
		}
		
		#endregion
		
		#region First/Single or specified

		[Pure]
		public static T FirstOrSpecified<T>(this IEnumerable<T> source, T valueIfEmpty)
		{
			using var enumerator = source.GetEnumerator();
			var firstValueExists = enumerator.MoveNext();
			return firstValueExists ? enumerator.Current : valueIfEmpty;
		}

		[Pure]
		public static T SingleOrSpecified<T>(this IEnumerable<T> source, T valueIfEmpty)
		{
			using var enumerator = source.GetEnumerator();
			var firstValueExists = enumerator.MoveNext();
			if (!firstValueExists)
			{
				return valueIfEmpty;
			}
			var firstValue = enumerator.Current;
			var secondValueExists = enumerator.MoveNext();
			if (secondValueExists)
			{
				throw new InvalidOperationException("Enumerable contains more than one element");
			}

			return firstValue;
		}
		
		#endregion

		#region KeyValuePair stuff

		[Pure]
		public static IEnumerable<KeyValuePair<int, T>> WithIndex<T>(this IEnumerable<T> source)
		{
			return source.Select((item, index) => KeyValuePair.Create(index, item));
		}
		
		[Pure]
		public static IEnumerable<KeyValuePair<TKey, TValue>> ZipKeyValue<TKey, TValue>(IEnumerable<TKey> keys, IEnumerable<TValue> values)
		{
			return keys.Zip(values, KeyValuePair.Create);
		}
		
		#endregion
		
		#region Multidimensional ForEach
		
		public delegate T ValueUpdater<T>(int[] indexes, T value);
		
		private static void ForEach<T>(this Array array, ValueUpdater<T> valueUpdater, int[] indexes)
		{
			var dimension = indexes.Length;
			if (dimension == array.Rank)
			{
				using (new ProfilerScope("Updating array value"))
				{
					var value = (T)array.GetValue(indexes);
					value = valueUpdater(indexes, value);
					array.SetValue(value, indexes);	
				}
				return;
			}
			
			for (var i = array.GetLowerBound(dimension); i <= array.GetUpperBound(dimension); i++)
			{
				var newIndexes = new int[dimension + 1];
				indexes.CopyTo(newIndexes, 0);
				newIndexes[^1] = i;
				ForEach(array, valueUpdater, newIndexes);
			}
		}
		
		public static void ForEach<T>(this Array array, ValueUpdater<T> valueUpdater)
		{
			using var profilerScope = new ProfilerScope("Multidimensional ForEach");
			
			var receivedType = array.GetType().GetElementType();
			var expectedType = typeof(T);
			if (receivedType != expectedType)
			{
				throw new ArrayTypeMismatchException($"Expected array of type {expectedType}, but got {receivedType}");
			}
			
			ForEach(array, valueUpdater, new int[] {});
		}
		
		#endregion

		#region 2D ForEach
		
		public delegate void ValueUpdater2D<T>(int index0, int index1, ref T value);
		
		public static void ForEach<T>(this T[,] matrix, ValueUpdater2D<T> valueUpdater)
		{
			using var profilerScope = new ProfilerScope("2D ForEach");
			for (var index0 = matrix.GetLowerBound(0); index0 <= matrix.GetUpperBound(0); index0++)
			{
				for (var index1 = matrix.GetLowerBound(1); index1 <= matrix.GetUpperBound(1); index1++)
				{
					using (new ProfilerScope("Updating array value"))
					{
						valueUpdater(index0, index1, ref matrix[index0, index1]);	
					}
				}
			}
		}
		
		#endregion
		
	}

	public class ProxyLookup<TKey, TMiddleValue, TFinalValue>
	{
		private readonly ILookup<TKey, TMiddleValue> _lookup;
		private readonly Func<TKey, IEnumerable<TMiddleValue>, TFinalValue> _getter;

		internal ProxyLookup(ILookup<TKey, TMiddleValue> lookup, Func<TKey, IEnumerable<TMiddleValue>, TFinalValue> getter)
		{
			_lookup = lookup;
			_getter = getter;
		}

		public TFinalValue this[TKey key] => _getter(key, _lookup[key]);
	}
}