Files
CPXV2/SystemX.Net.CP.Platform/SystemX.Net.Platform/SystemX.Common/Extension/EmLinq.cs
2024-06-26 10:30:00 +09:00

375 lines
11 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace SystemX.Net.Platform.Common.ExtensionMethods
{
/// <summary>
/// Linq extension methods
/// </summary>
public static partial class EmLinq
{
// http://stackoverflow.com/questions/1883920/call-a-function-for-each-value-in-a-generic-c-sharp-collection
/// <summary>
/// Generic IEnumerable ForEach extension : typename T is optional. deducible from source
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="source"></param>
/// <param name="action"></param>
public static void ForEach<T>(this IEnumerable<T> source, Action<T> action)
{
foreach (T item in source)
action(item);
}
public static void Iter<T>(this IEnumerable<T> source, Action<T> action) => ForEach(source, action);
public static void Iter<T>(this IEnumerable<T> source, Action<T, int> action)
{
int i = 0;
foreach (T item in source)
action(item, i++);
}
/// <summary>
/// Lazy Foreach : Not evaluated until on demand
/// </summary>
public static IEnumerable<T> ForEachTee<T>(this IEnumerable<T> source, Action<T> action)
{
foreach (T item in source)
{
action(item);
yield return item;
}
}
/// <summary>
/// Non-Generic IEnumerable ForEach extension : typename T should be provided.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="source"></param>
/// <param name="action"></param>
public static void ForEach<T>(this IEnumerable source, Action<T> action)
{
foreach (T item in source)
action(item);
}
/// <summary>
/// Non-Generic IEnumerable ForEach extension : typename T should be provided.
/// Lazy Foreach : Not evaluated until on demand
/// </summary>
public static IEnumerable ForEachTee<T>(this IEnumerable source, Action<T> action)
{
foreach (T item in source)
{
action(item);
yield return item;
}
}
/// <summary>
/// Non-Generic IEnumerable Select extension : typename T should be provided.
/// </summary>
/// <typeparam name="TSource"></typeparam>
/// <typeparam name="TResult"></typeparam>
/// <param name="source"></param>
/// <param name="selector"></param>
/// <returns></returns>
public static IEnumerable<TResult> SelectEx<TSource, TResult>(this IEnumerable source, Func<TSource, TResult> selector)
{
foreach (TSource item in source)
yield return selector(item);
}
/// <summary>
/// TResult type 의 enumerable 중에서 TNotCheckType type 이 아닌 것들만 골라서 반환한다. (System.Linq.OfType 의 negation)
/// <para/> System.Linq.SkipWhile() 구문과 같은 역할
/// <para/> TNotCheck 는 TResult type 이어야 함.
/// </summary>
/// <typeparam name="TResult">enumerable 들의 base type. 동시에 반환될 enumerable 의 type</typeparam>
/// <typeparam name="TNotCheckType">제외할 type</typeparam>
/// <param name="source"></param>
/// <returns></returns>
public static IEnumerable<TResult> OfNotType<TResult, TNotCheckType>(this IEnumerable source) where TNotCheckType : TResult
{
if (source == null) throw new ArgumentNullException("source");
return OfNotTypeIterator<TResult, TNotCheckType>(source);
}
private static IEnumerable<TResult> OfNotTypeIterator<TResult, TNotCheckType>(IEnumerable source)
{
foreach (object obj in source)
{
if (!(obj is TNotCheckType))
yield return (TResult)obj;
}
}
/// <summary>
/// Select Non null element from enumerable
/// </summary>
public static IEnumerable<TResult> OfNotNull<TResult>(this IEnumerable<TResult> source) where TResult : class
{
if (source == null) throw new ArgumentNullException("source");
foreach (var s in source)
{
if ( s != null )
yield return s;
}
}
// http://stackoverflow.com/questions/2471588/how-to-get-index-using-linq
public static Nullable<int> FindIndex<T>(this IEnumerable<T> items, Predicate<T> predicate)
{
int index = 0;
foreach (var item in items)
{
if (predicate(item))
return index;
index++;
}
return null;
}
public static HashSet<T> ToHashSet<T>(this IEnumerable<T> items)
{
var hash = new HashSet<T>();
items.ForEach(i => hash.Add(i));
return hash;
}
/// <summary>
/// 두개의 set 이 동일한지 비교. see SequenceEqual
/// </summary>
public static bool SetEqual<T>(this IEnumerable<T> first, IEnumerable<T> second)
{
HashSet<T> firstSet = first.ToHashSet();
foreach (var e in second)
{
if (!firstSet.Contains(e))
return false;
}
HashSet<T> secondSet = second.ToHashSet();
foreach (var e in first)
{
if (!secondSet.Contains(e))
return false;
}
return true;
}
public static bool RemoveTail(this IList list)
{
if (list.IsNullOrEmpty())
return false;
list.RemoveAt(list.Count - 1);
return true;
}
public static T[] ConcatArrays<T>(params T[][] list)
{
var result = new T[list.Sum(a => a.Length)];
int offset = 0;
for (int x = 0; x < list.Length; x++)
{
list[x].CopyTo(result, offset);
offset += list[x].Length;
}
return result;
}
private static Tuple<bool, T> ExtractFirst<T>(this IEnumerable<T> seq)
{
using (var enumerator = seq.GetEnumerator())
{
if (enumerator.MoveNext())
return Tuple.Create(true, enumerator.Current); // => return new Tuple<bool, T>(..) 와 동일
return Tuple.Create(false, default(T));
}
}
/// <summary>
/// http://stackoverflow.com/questions/4354902/check-that-all-items-of-ienumerablet-has-the-same-value-using-linq
/// </summary>
public static bool AllEqual<T>(this IEnumerable<T> source, T target)
{
using (var enumerator = source.GetEnumerator())
{
if (!enumerator.MoveNext())
{
// empty case
return true;
}
var comparer = EqualityComparer<T>.Default;
do
{
if (!comparer.Equals(target, enumerator.Current))
return false;
} while (enumerator.MoveNext());
return true;
}
}
public static bool AllEqual<T>(this IEnumerable<T> source)
{
var pr = source.ExtractFirst();
if (pr.Item1)
return AllEqual(source, pr.Item2);
// empty case
return true;
}
public static T Tee<T>(this T input, Action action)
{
action();
return input;
}
public static T Tee<T>(this T input, Action<T> action)
{
action(input);
return input;
}
public static bool ForAll<T>(this IEnumerable<T> source, Func<T, bool> predicate)
{
foreach (var s in source)
{
if (!predicate(s))
return false;
}
return true;
}
public static bool ForAll<T>(this IEnumerable<T> source, Func<T, int, bool> predicate)
{
int i = 0;
foreach (var s in source)
{
if (!predicate(s, i++))
return false;
}
return true;
}
public static bool NoForAll<T>(this IEnumerable<T> source, Func<T, bool> predicate)
{
foreach (var s in source)
{
if (predicate(s))
return false;
}
return true;
}
/// <summary>
/// source 를 n 개씩 분할한 sequence 를 반환
/// http://stackoverflow.com/questions/419019/split-list-into-sublists-with-linq
/// </summary>
public static IEnumerable<IEnumerable<T>> SplitByN<T>(this IEnumerable<T> source, int n)
{
return source
.Select((x, i) => new { Index = i, Value = x })
.GroupBy(x => x.Index / n)
.Select(x => x.Select(v => v.Value))
;
}
public static IEnumerable<T> EveryNth<T>(this IEnumerable<T> source, int n)
{
return source.Where((v, i) => i % n == 0);
}
public static IEnumerable<TResult> Zip2<TS1, TS2, TResult>(
this IEnumerable<TS1> s1,
IEnumerable<TS2> s2,
Func<TS1, TS2, TResult> resultSelector) => s1.Zip(s2, resultSelector);
public static IEnumerable<TResult> Zip3<TS1, TS2, TS3, TResult>(
this IEnumerable<TS1> s1,
IEnumerable<TS2> s2,
IEnumerable<TS3> s3,
Func<TS1, TS2, TS3, TResult> resultSelector)
{
using (var e1 = s1.GetEnumerator())
using (var e2 = s2.GetEnumerator())
using (var e3 = s3.GetEnumerator())
{
while (e1.MoveNext() && e2.MoveNext() && e3.MoveNext())
yield return resultSelector(e1.Current, e2.Current, e3.Current);
}
}
public static IEnumerable<TResult> Zip4<TS1, TS2, TS3, TS4, TResult>(
this IEnumerable<TS1> s1,
IEnumerable<TS2> s2,
IEnumerable<TS3> s3,
IEnumerable<TS4> s4,
Func<TS1, TS2, TS3, TS4, TResult> resultSelector)
{
using (var e1 = s1.GetEnumerator())
using (var e2 = s2.GetEnumerator())
using (var e3 = s3.GetEnumerator())
using (var e4 = s4.GetEnumerator())
{
while (e1.MoveNext() && e2.MoveNext() && e3.MoveNext() && e4.MoveNext())
yield return resultSelector(e1.Current, e2.Current, e3.Current, e4.Current);
}
}
public static IEnumerable<int> MinMaxRange(int min, int hop, int max)
{
if (hop <= 0)
throw new ArgumentException("hop counter should be positive.");
for (int i = min; i <= max; i += hop)
yield return i;
}
public static IEnumerable<int> MinMaxRange(int min, int max) => Enumerable.Range(min, max - min);
public static IEnumerable<string> HexRange(int start, int count)
{
foreach (var h in Enumerable.Range(start, count))
{
yield return $"{h:X}";
}
}
/// <summary>
/// Enumerable 의 laziness 를 강제로 실행시켜 evaluation 시킴.
/// </summary>
public static IEnumerable<T> Realize<T>(this IEnumerable<T> seq)
{
//var count = seq.Count(); // count 만으로는, 즉석 evaluation 이 안되는 경우가 존재...???...
var array = seq.ToArray();
return array;
}
}
}