Skip to content

Instantly share code, notes, and snippets.

@VienosNotes
Created August 25, 2016 02:58
Show Gist options
  • Save VienosNotes/c9769242dafae1968f7cf403fd8083cd to your computer and use it in GitHub Desktop.
Save VienosNotes/c9769242dafae1968f7cf403fd8083cd to your computer and use it in GitHub Desktop.
using System;
using System.Collections.Generic;
using System.Linq;
namespace MyLinq
{
/// <summary>
/// LINQ拡張メソッドを定義するためのユーティリティクラスです。
/// </summary>
public static class LinqEx
{
/// <summary>
/// コレクションの要素を先頭から順にN個ずつとりだした部分列に分割します(SelectManyの逆操作)。要素数がNの倍数でない場合はdefault(TSource)で補填されます。
/// </summary>
/// <typeparam name="TSource">ソースコレクションの要素の型。</typeparam>
/// <param name="source">ソースコレクション。</param>
/// <param name="takeCount">一度に何個ずつ取り出すか。省略した場合は既定値2が使用されます。</param>
/// <param name="padding"></param>
/// <returns>もとの要素がN個ずつに分割されたコレクションのコレクション。</returns>
public static IEnumerable<IEnumerable<TSource>> ZipSelf<TSource>(this IEnumerable<TSource> source, int takeCount = 2, bool padding = true)
{
if (takeCount < 1)
{
throw new ArgumentOutOfRangeException("takeCount は1以上の整数である必要があります。");
}
return ZipSelfIterator<TSource>(source, takeCount, padding);
}
/// <summary>
/// ZipSelfの実装。
/// </summary>
/// <typeparam name="TSource">ソースコレクションの要素の型。</typeparam>
/// <param name="source">ソースコレクション。</param>
/// <param name="takeCount">一度に何個ずつ取り出すか。省略した場合は既定値2が使用されます。</param>
/// <param name="padding"></param>
/// <returns>もとの要素がN個ずつに分割されたコレクションのコレクション。</returns>
private static IEnumerable<IEnumerable<TSource>> ZipSelfIterator<TSource>(this IEnumerable<TSource> source, int takeCount, bool padding)
{
IEnumerator<TSource> en = source.GetEnumerator();
while (en.MoveNext())
{
var curList = new List<TSource>();
curList.Add(en.Current);
for (var i = 0; i < takeCount - 1; i++)
{
if (en.MoveNext())
{
curList.Add(en.Current);
}
else
{
if (padding)
{
curList.Add(default(TSource));
}
}
}
yield return curList;
}
}
/// <summary>
/// 複数のシーケンスから要素を1つずつ取り出して、それらにセレクタを適用したシーケンスを返します。
/// Enumerable.Zip() との違いは、各入力シーケンスの長さが一致しない場合に短いシーケンスの不足要素が default(TSource) で補填された状態でセレクタを適用する点です。
/// </summary>
/// <typeparam name="TSource">入力シーケンスの要素の型。</typeparam>
/// <typeparam name="TResult">セレクタによって射影された型。</typeparam>
/// <param name="selector">入力シーケンスの要素を受け取って射影するセレクタ。このセレクタは default(TSource) を引数の要素として受け取ることを想定する必要があります。</param>
/// <param name="sources">入力シーケンスの列。</param>
/// <returns>射影されたシーケンス。このシーケンスの要素数は、入力シーケンスの中で一番要素数が多いものの要素数と一致します。</returns>
public static IEnumerable<TResult> MultipleZip<TSource, TResult>(Func<IEnumerable<TSource>, TResult> selector, params IEnumerable<TSource>[] sources)
{
if (sources == null || sources.Length == 0)
{
return new List<TResult>();
}
if (selector == null)
{
throw new ArgumentException("selectorはnullを許容しません。");
}
return MultipleZipIterator(selector, sources);
}
/// <summary>
///
/// </summary>
/// <typeparam name="TSource"></typeparam>
/// <typeparam name="TResult"></typeparam>
/// <param name="selector"></param>
/// <param name="sources"></param>
/// <returns></returns>
private static IEnumerable<TResult> MultipleZipIterator<TSource, TResult>(Func<IEnumerable<TSource>, TResult> selector, params IEnumerable<TSource>[] sources)
{
var idx = 0;
var ss = sources.Select(s => s.ToList());
while (true)
{
var list = new List<TSource>();
foreach (var s in ss)
{
if (idx < s.Count)
{
list.Add(s[idx]);
}
else
{
list.Add(default(TSource));
}
}
if (list.All(item => item.Equals(default(TSource))))
{
yield break;
}
else
{
yield return selector(list);
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment