web-dev-qa-db-ja.com

組み込みではない、お気に入りのLINQ to Objects演算子は何ですか?

拡張メソッドを使用すると、一般的な問題を解決する便利なLINQ演算子を記述できます。

System.Linq名前空間で欠落しているメソッドまたはオーバーロードと、それらをどのように実装したかを知りたいです。

おそらく既存の方法を使用した、クリーンでエレガントな実装が推奨されます。

71
Nappy

追加と追加

/// <summary>Adds a single element to the end of an IEnumerable.</summary>
/// <typeparam name="T">Type of enumerable to return.</typeparam>
/// <returns>IEnumerable containing all the input elements, followed by the
/// specified additional element.</returns>
public static IEnumerable<T> Append<T>(this IEnumerable<T> source, T element)
{
    if (source == null)
        throw new ArgumentNullException("source");
    return concatIterator(element, source, false);
}

/// <summary>Adds a single element to the start of an IEnumerable.</summary>
/// <typeparam name="T">Type of enumerable to return.</typeparam>
/// <returns>IEnumerable containing the specified additional element, followed by
/// all the input elements.</returns>
public static IEnumerable<T> Prepend<T>(this IEnumerable<T> tail, T head)
{
    if (tail == null)
        throw new ArgumentNullException("tail");
    return concatIterator(head, tail, true);
}

private static IEnumerable<T> concatIterator<T>(T extraElement,
    IEnumerable<T> source, bool insertAtStart)
{
    if (insertAtStart)
        yield return extraElement;
    foreach (var e in source)
        yield return e;
    if (!insertAtStart)
        yield return extraElement;
}
31
Timwi

MoreLINQプロジェクト についてまだ誰も言及していないことに驚いています。それは Jon Skeet によって開始され、途中で何人かの開発者を獲得しました。プロジェクトのページから:

LINQ to Objectsには、いくつかの望ましい機能がありません。

このプロジェクトは、LINQの精神を維持する方法で、追加のメソッドを使用してLINQ toObjectsを拡張します。

実装されている演算子のリストについては、 演算子の概要 wikiページをご覧ください。

クリーンでエレガントなソースコードから学ぶのは確かに良い方法です。

21
Ahmad Mageed

純粋主義者には何もありませんが、それは便利です!

 public static void Each<T>(this IEnumerable<T> items, Action<T> action)
 {
   foreach (var i in items)
      action(i);
 }
16
flq

ToQueueとToStack

/// <summary>Creates a <see cref="Queue&lt;T&gt;"/> from an enumerable
/// collection.</summary>
public static Queue<T> ToQueue<T>(this IEnumerable<T> source)
{
    if (source == null)
        throw new ArgumentNullException("source");
    return new Queue<T>(source);
}

/// <summary>Creates a <see cref="Stack&lt;T&gt;"/> from an enumerable
/// collection.</summary>
public static Stack<T> ToStack<T>(this IEnumerable<T> source)
{
    if (source == null)
        throw new ArgumentNullException("source");
    return new Stack<T>(source);
}
14
Timwi

IsEmpty

public static bool IsEmpty<T>(this IEnumerable<T> source)
{
    return !source.Any();
}
13
Mashmagar

InとNotIn

他の2つのよく知られたSQL構造に相当するC#

/// <summary>
/// Determines if the source value is contained in the list of possible values.
/// </summary>
/// <typeparam name="T">The type of the objects</typeparam>
/// <param name="value">The source value</param>
/// <param name="values">The list of possible values</param>
/// <returns>
///     <c>true</c> if the source value matches at least one of the possible values; otherwise, <c>false</c>.
/// </returns>
public static bool In<T>(this T value, params T[] values)
{
    if (values == null)
        return false;

    if (values.Contains<T>(value))
        return true;

    return false;
}

/// <summary>
/// Determines if the source value is contained in the list of possible values.
/// </summary>
/// <typeparam name="T">The type of the objects</typeparam>
/// <param name="value">The source value</param>
/// <param name="values">The list of possible values</param>
/// <returns>
///     <c>true</c> if the source value matches at least one of the possible values; otherwise, <c>false</c>.
/// </returns>
public static bool In<T>(this T value, IEnumerable<T> values)
{
    if (values == null)
        return false;

    if (values.Contains<T>(value))
        return true;

    return false;
}

/// <summary>
/// Determines if the source value is not contained in the list of possible values.
/// </summary>
/// <typeparam name="T">The type of the objects</typeparam>
/// <param name="value">The source value</param>
/// <param name="values">The list of possible values</param>
/// <returns>
///     <c>false</c> if the source value matches at least one of the possible values; otherwise, <c>true</c>.
/// </returns>
public static bool NotIn<T>(this T value, params T[] values)
{
    return In(value, values) == false;
}

/// <summary>
/// Determines if the source value is not contained in the list of possible values.
/// </summary>
/// <typeparam name="T">The type of the objects</typeparam>
/// <param name="value">The source value</param>
/// <param name="values">The list of possible values</param>
/// <returns>
///     <c>false</c> if the source value matches at least one of the possible values; otherwise, <c>true</c>.
/// </returns>
public static bool NotIn<T>(this T value, IEnumerable<T> values)
{
    return In(value, values) == false;
}
12
Johann Blais

JoinString

基本的にstring.Joinと同じですが、次のようになります。

  • 文字列のコレクションだけでなく、任意のコレクションで使用する機能を備えています(すべての要素でToStringを呼び出します)

  • すべての文字列にプレフィックスとサフィックスを追加する機能を備えています。

  • 拡張メソッドとして。 string.Joinは静的であるため、煩わしいと思います。つまり、一連の操作では、字句的に正しい順序ではありません。


/// <summary>
/// Turns all elements in the enumerable to strings and joins them using the
/// specified string as the separator and the specified prefix and suffix for
/// each string.
/// <example>
///   <code>
///     var a = (new[] { "Paris", "London", "Tokyo" }).JoinString(", ", "[", "]");
///     // a contains "[Paris], [London], [Tokyo]"
///   </code>
/// </example>
/// </summary>
public static string JoinString<T>(this IEnumerable<T> values,
    string separator = null, string prefix = null, string suffix = null)
{
    if (values == null)
        throw new ArgumentNullException("values");

    using (var enumerator = values.GetEnumerator())
    {
        if (!enumerator.MoveNext())
            return "";
        StringBuilder sb = new StringBuilder();
        sb.Append(prefix).Append(enumerator.Current.ToString()).Append(suffix);
        while (enumerator.MoveNext())
            sb.Append(separator).Append(prefix)
              .Append(enumerator.Current.ToString()).Append(suffix);
        return sb.ToString();
    }
}
9
Timwi

AsIEnumerable

/// <summary>
/// Returns a sequence containing one element.
/// </summary>
public static IEnumerable<T> AsIEnumerable<T>(this T obj)
{
    yield return obj;
}  

使用法

var nums = new[] {12, 20, 6};
var numsWith5Prepended = 5.AsIEnumerable().Concat(nums);   
9
Ani

注文

/// <summary>Sorts the elements of a sequence in ascending order.</summary>
public static IEnumerable<T> Order<T>(this IEnumerable<T> source)
{
    return source.OrderBy(x => x);
}
8
Timwi

Shuffle

public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> items)
{
    var random = new Random();
    return items.OrderBy(x => random.Next());
}

編集:上記の実装にはいくつかの問題があるようです。これは、@ LukeHのコードと@ckおよび@Strilancからのコメントに基づいた改良版です。

private static Random _Rand = new Random();
public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source)
{
    var items = source == null ? new T[] { } : source.ToArray();
    var count = items.Length;
    while(count > 0)
    {
        int toReturn = _Rand.Next(0, count);
        yield return items[toReturn];
        items[toReturn] = items[count - 1];
        count--;
    }
}
8
Handcraftsman

MinElement

Minは、指定された式によって返される最小値のみを返しますが、この最小要素を与えた元の要素は返しません。

/// <summary>Returns the first element from the input sequence for which the
/// value selector returns the smallest value.</summary>
public static T MinElement<T, TValue>(this IEnumerable<T> source,
        Func<T, TValue> valueSelector) where TValue : IComparable<TValue>
{
    if (source == null)
        throw new ArgumentNullException("source");
    if (valueSelector == null)
        throw new ArgumentNullException("valueSelector");
    using (var enumerator = source.GetEnumerator())
    {
        if (!enumerator.MoveNext())
            throw new InvalidOperationException("source contains no elements.");
        T minElem = enumerator.Current;
        TValue minValue = valueSelector(minElem);
        while (enumerator.MoveNext())
        {
            TValue value = valueSelector(enumerator.Current);
            if (value.CompareTo(minValue) < 0)
            {
                minValue = value;
                minElem = enumerator.Current;
            }
        }
        return minElem;
    }
}
7
Timwi

Loop

これは私がちょうど考えたちょっとクールなものです。 (もし私がちょうど考えたなら、それはそれほど役に立たないかもしれませんか?しかし、私はそれを使用するのでそれを考えました。)シーケンスを繰り返しループして無限のシーケンスを生成します。これにより、Enumerable.RangeおよびEnumerable.Repeatが提供するようなものが実現されますが、任意Rangeとは異なります)シーケンスとは異なります)Repeatとは異なります):

public static IEnumerable<T> Loop<T>(this IEnumerable<T> source)
{
    while (true)
    {
        foreach (T item in source)
        {
            yield return item;
        }
    }
}

使用法:

var numbers = new[] { 1, 2, 3 };
var looped = numbers.Loop();

foreach (int x in looped.Take(10))
{
    Console.WriteLine(x);
}

出力:

 1 
 2 
 3 
 1 
 2 
 3 
 1 
 2 
 3 
 1 

注:次のような方法でもこれを達成できると思います。

var looped = Enumerable.Repeat(numbers, int.MaxValue).SelectMany(seq => seq);

...しかし、Loopの方が明確だと思います。

7
Dan Tao

の指標

/// <summary>
/// Returns the index of the first element in this <paramref name="source"/>
/// satisfying the specified <paramref name="condition"/>. If no such elements
/// are found, returns -1.
/// </summary>
public static int IndexOf<T>(this IEnumerable<T> source, Func<T, bool> condition)
{
    if (source == null)
        throw new ArgumentNullException("source");
    if (condition == null)
        throw new ArgumentNullException("condition");
    int index = 0;
    foreach (var v in source)
    {
        if (condition(v))
            return index;
        index++;
    }
    return -1;
}
6
Timwi

チャンク

特定のサイズのチャンクを返します。 1,2,3,4,5x.Chunks(2)は、1,23,4の2つの配列を返します。 x.Chunks(2,true)は、1,23,4、および5を返します。

public static IEnumerable<T[]> Chunks<T>(this IEnumerable<T> xs, int size, bool returnRest = false)
{
    var curr = new T[size];

    int i = 0;

    foreach (var x in xs)
    {
        if (i == size)
        {
            yield return curr;
            i = 0;
            curr = new T[size];
        }

        curr[i++] = x;
    }

    if (returnRest)
        yield return curr.Take(i).ToArray();
}
6
Lasse Espeholt

ToHashSet

public static HashSet<T> ToHashSet<T>(this IEnumerable<T> items)
{
    return new HashSet<T>(items);
}
6
Handcraftsman

デフォルト値が指定されたFirstOrDefault

/// <summary>
/// Returns the first element of a sequence, or a default value if the
/// sequence contains no elements.
/// </summary>
/// <typeparam name="T">The type of the elements of
/// <paramref name="source"/>.</typeparam>
/// <param name="source">The <see cref="IEnumerable&lt;T&gt;"/> to return
/// the first element of.</param>
/// <param name="default">The default value to return if the sequence contains
/// no elements.</param>
/// <returns><paramref name="default"/> if <paramref name="source"/> is empty;
/// otherwise, the first element in <paramref name="source"/>.</returns>
public static T FirstOrDefault<T>(this IEnumerable<T> source, T @default)
{
    if (source == null)
        throw new ArgumentNullException("source");
    using (var e = source.GetEnumerator())
    {
        if (!e.MoveNext())
            return @default;
        return e.Current;
    }
}

/// <summary>
/// Returns the first element of a sequence, or a default value if the sequence
/// contains no elements.
/// </summary>
/// <typeparam name="T">The type of the elements of
/// <paramref name="source"/>.</typeparam>
/// <param name="source">The <see cref="IEnumerable&lt;T&gt;"/> to return
/// the first element of.</param>
/// <param name="predicate">A function to test each element for a
/// condition.</param>
/// <param name="default">The default value to return if the sequence contains
/// no elements.</param>
/// <returns><paramref name="default"/> if <paramref name="source"/> is empty
/// or if no element passes the test specified by <paramref name="predicate"/>;
/// otherwise, the first element in <paramref name="source"/> that passes
/// the test specified by <paramref name="predicate"/>.</returns>
public static T FirstOrDefault<T>(this IEnumerable<T> source,
    Func<T, bool> predicate, T @default)
{
    if (source == null)
        throw new ArgumentNullException("source");
    if (predicate == null)
        throw new ArgumentNullException("predicate");
    using (var e = source.GetEnumerator())
    {
        while (true)
        {
            if (!e.MoveNext())
                return @default;
            if (predicate(e.Current))
                return e.Current;
        }
    }
}
5
Timwi

InsertBetween

連続する要素のすべてのペアの間に要素を挿入します。

/// <summary>Inserts the specified item in between each element in the input
/// collection.</summary>
/// <param name="source">The input collection.</param>
/// <param name="extraElement">The element to insert between each consecutive
/// pair of elements in the input collection.</param>
/// <returns>A collection containing the original collection with the extra
/// element inserted. For example, new[] { 1, 2, 3 }.InsertBetween(0) returns
/// { 1, 0, 2, 0, 3 }.</returns>
public static IEnumerable<T> InsertBetween<T>(
    this IEnumerable<T> source, T extraElement)
{
    return source.SelectMany(val => new[] { extraElement, val }).Skip(1);
}
5
Timwi

解析

これには、要素をスキップして文字列のシーケンスを値のシーケンスに解析するために使用されるカスタムデリゲート(代わりにIParser<T>インターフェイスを使用できましたが、デリゲートを使用した方が簡単でした)が含まれます解析が失敗する場所。

public delegate bool TryParser<T>(string text, out T value);

public static IEnumerable<T> Parse<T>(this IEnumerable<string> source,
                                      TryParser<T> parser)
{
    source.ThrowIfNull("source");
    parser.ThrowIfNull("parser");

    foreach (string str in source)
    {
        T value;
        if (parser(str, out value))
        {
            yield return value;
        }
    }
}

使用法:

var strings = new[] { "1", "2", "H3llo", "4", "five", "6", "se7en" };
var numbers = strings.Parse<int>(int.TryParse);

foreach (int x in numbers)
{
    Console.WriteLine(x);
}

出力:

 1 
 2 
 4 
 6 

これにはネーミングが難しい。 Parseが最良のオプションであるかどうか(少なくともis単純)、またはParseWhereValidのようなものがより良いかどうかはわかりません。

5
Dan Tao

EmptyIfNull

これは物議を醸すものです。多くの純粋主義者は、nullの成功に関する「インスタンスメソッド」に反対すると確信しています。

/// <summary>
/// Returns an IEnumerable<T> as is, or an empty IEnumerable<T> if it is null
/// </summary>
public static IEnumerable<T> EmptyIfNull<T>(this IEnumerable<T> source)
{
    return source ?? Enumerable.Empty<T>();
}    

使用法:

foreach(var item in myEnumerable.EmptyIfNull())
{
  Console.WriteLine(item);   
}
5
Ani

ZipMerge

これは私のバージョンのZipで、本物のジッパーのように機能します。 2つの値を1つに投影しませんが、結合されたIEnumerableを返します。過負荷、右および/または左テールのスキップが可能です。

public static IEnumerable<TSource> ZipMerge<TSource>(
        this IEnumerable<TSource> first,
        IEnumerable<TSource> second)
{
    using (var secondEnumerator = second.GetEnumerator())
    {
        foreach (var item in first)
        {
            yield return item;

            if (secondEnumerator.MoveNext())
                yield return secondEnumerator.Current;
        }

        while (secondEnumerator.MoveNext())
            yield return secondEnumerator.Current;
    }
}
4
Nappy

AssertCount

IEnumerable<T>に少なくとも/正確に/最大で特定の数の要素が含まれているかどうかを効率的に判断します。

public enum CountAssertion
{
    AtLeast,
    Exact,
    AtMost
}

/// <summary>
/// Asserts that the number of items in a sequence matching a specified predicate satisfies a specified CountAssertion.
/// </summary>
public static bool AssertCount<T>(this IEnumerable<T> source, int countToAssert, CountAssertion assertion, Func<T, bool> predicate)
{
    if (source == null)
        throw new ArgumentNullException("source");

    if (predicate == null)
        throw new ArgumentNullException("predicate");

    return source.Where(predicate).AssertCount(countToAssert, assertion);
}

/// <summary>
/// Asserts that the number of elements in a sequence satisfies a specified CountAssertion.
/// </summary>
public static bool AssertCount<T>(this IEnumerable<T> source, int countToAssert, CountAssertion assertion)
{
    if (source == null)
        throw new ArgumentNullException("source");

    if (countToAssert < 0)
        throw new ArgumentOutOfRangeException("countToAssert");    

    switch (assertion)
    {
        case CountAssertion.AtLeast:
            return AssertCountAtLeast(source, GetFastCount(source), countToAssert);

        case CountAssertion.Exact:
            return AssertCountExact(source, GetFastCount(source), countToAssert);

        case CountAssertion.AtMost:
            return AssertCountAtMost(source, GetFastCount(source), countToAssert);

        default:
            throw new ArgumentException("Unknown CountAssertion.", "assertion");
    }

}

private static int? GetFastCount<T>(IEnumerable<T> source)
{
    var genericCollection = source as ICollection<T>;
    if (genericCollection != null)
        return genericCollection.Count;

    var collection = source as ICollection;
    if (collection != null)
        return collection.Count;

    return null;
}

private static bool AssertCountAtMost<T>(IEnumerable<T> source, int? fastCount, int countToAssert)
{
    if (fastCount.HasValue)
        return fastCount.Value <= countToAssert;

    int countSoFar = 0;

    foreach (var item in source)
    {
        if (++countSoFar > countToAssert) return false;
    }

    return true;
}

private static bool AssertCountExact<T>(IEnumerable<T> source, int? fastCount, int countToAssert)
{
    if (fastCount.HasValue)
        return fastCount.Value == countToAssert;

    int countSoFar = 0;

    foreach (var item in source)
    {
        if (++countSoFar > countToAssert) return false;
    }

    return countSoFar == countToAssert;
}

private static bool AssertCountAtLeast<T>(IEnumerable<T> source, int? fastCount, int countToAssert)
{
    if (countToAssert == 0)
        return true;

    if (fastCount.HasValue)
        return fastCount.Value >= countToAssert;

    int countSoFar = 0;

    foreach (var item in source)
    {
        if (++countSoFar >= countToAssert) return true;
    }

    return false;
}

使用法

var nums = new[] { 45, -4, 35, -12, 46, -98, 11 };
bool hasAtLeast3Positive = nums.AssertCount(3, CountAssertion.AtLeast, i => i > 0); //true
bool hasAtMost1Negative = nums.AssertCount(1, CountAssertion.AtMost, i => i < 0); //false
bool hasExactly2Negative = nums.AssertCount(2, CountAssertion.Exact, i => i < 0); //false
4
Ani

One、Two、MoreThanOne、AtLeast、AnyAtAll

public static bool One<T>(this IEnumerable<T> enumerable)
{
    using (var enumerator = enumerable.GetEnumerator())
        return enumerator.MoveNext() && !enumerator.MoveNext();
}

public static bool Two<T>(this IEnumerable<T> enumerable)
{
    using (var enumerator = enumerable.GetEnumerator())
        return enumerator.MoveNext() && enumerator.MoveNext() && !enumerator.MoveNext();
}

public static bool MoreThanOne<T>(this IEnumerable<T> enumerable)
{
    return enumerable.Skip(1).Any();
}

public static bool AtLeast<T>(this IEnumerable<T> enumerable, int count)
{
    using (var enumerator = enumerable.GetEnumerator())
        for (var i = 0; i < count; i++)
            if (!enumerator.MoveNext())
                return false;
    return true;
}

public static bool AnyAtAll<T>(this IEnumerable<T> enumerable)
{
    return enumerable != null && enumerable.Any();
}
4
noopman

RandomSample

これは、中規模から大規模のデータセット(たとえば、100を超えるアイテム)があり、そのランダムなサンプリングだけを目で確認したい場合に便利な単純な関数です。

public static IEnumerable<T> RandomSample<T>(this IEnumerable<T> source,
                                             double percentage)
{
    source.ThrowIfNull("source");

    var r = new Random();
    return source.Where(x => (r.NextDouble() * 100.0) < percentage);
}

使用法:

List<DataPoint> data = GetData();

// Sample roughly 3% of the data
var sample = data.RandomSample(3.0);

// Verify results were correct for this sample
foreach (DataPoint point in sample)
{
    Console.WriteLine("{0} => {1}", point, DoCalculation(point));
}

ノート:

  1. 返されるアイテムの数は確率的であるため、小さなコレクションにはあまり適していません(小さなシーケンスで簡単にゼロを返す可能性があります)。
  2. シーケンス内のすべてのアイテムを列挙する必要があるため、膨大なコレクションやデータベースクエリにはあまり適していません。
4
Dan Tao

最新の値を含むsizeの長さの配列( "windows")を列挙します。
{ 0, 1, 2, 3 }{ [0, 1], [1, 2], [2, 3] }になります。

たとえば、これを使用して、2点を接続して折れ線グラフを描画しています。

public static IEnumerable<TSource[]> Window<TSource>(
    this IEnumerable<TSource> source)
{
    return source.Window(2);
}

public static IEnumerable<TSource[]> Window<TSource>(
    this IEnumerable<TSource> source, int size)
{
    if (size <= 0)
        throw new ArgumentOutOfRangeException("size");

    return source.Skip(size).WindowHelper(size, source.Take(size));
}

private static IEnumerable<TSource[]> WindowHelper<TSource>(
    this IEnumerable<TSource> source, int size, IEnumerable<TSource> init)
{
    Queue<TSource> q = new Queue<TSource>(init);

    yield return q.ToArray();

    foreach (var value in source)
    {
        q.Dequeue();
        q.Enqueue(value);
        yield return q.ToArray();
    }
}
4
Nappy

WhereIf

WhereおよびIEnumerableのオプションのIQueryable句。クエリの述語とラムダを作成するときにifステートメントを回避します。コンパイル時にフィルターを適用する必要があるかどうかわからない場合に便利です。

public static IEnumerable<TSource> WhereIf<TSource>(
            this IEnumerable<TSource> source, bool condition,
            Func<TSource, bool> predicate)
{
    return condition ? source.Where(predicate) : source;
}

使用法:

var custs = Customers.WhereIf(someBool, x=>x.EyeColor=="Green");

LINQ WhereIf At ExtensionMethod.NET そして Andrewのブログ から借用しました。

3
p.campbell

SkipLast&TakeLast

/// <summary>
/// Enumerates the items of this collection, skipping the last
/// <paramref name="count"/> items. Note that the memory usage of this method
/// is proportional to <paramref name="count"/>, but the source collection is
/// only enumerated once, and in a lazy fashion. Also, enumerating the first
/// item will take longer than enumerating subsequent items.
/// </summary>
public static IEnumerable<T> SkipLast<T>(this IEnumerable<T> source, int count)
{
    if (source == null)
        throw new ArgumentNullException("source");
    if (count < 0)
        throw new ArgumentOutOfRangeException("count",
            "count cannot be negative.");
    if (count == 0)
        return source;
    return skipLastIterator(source, count);
}
private static IEnumerable<T> skipLastIterator<T>(IEnumerable<T> source,
    int count)
{
    var queue = new T[count];
    int headtail = 0; // tail while we're still collecting, both head & tail
                      // afterwards because the queue becomes completely full
    int collected = 0;

    foreach (var item in source)
    {
        if (collected < count)
        {
            queue[headtail] = item;
            headtail++;
            collected++;
        }
        else
        {
            if (headtail == count) headtail = 0;
            yield return queue[headtail];
            queue[headtail] = item;
            headtail++;
        }
    }
}

/// <summary>
/// Returns a collection containing only the last <paramref name="count"/>
/// items of the input collection. This method enumerates the entire
/// collection to the end once before returning. Note also that the memory
/// usage of this method is proportional to <paramref name="count"/>.
/// </summary>
public static IEnumerable<T> TakeLast<T>(this IEnumerable<T> source, int count)
{
    if (source == null)
        throw new ArgumentNullException("source");
    if (count < 0)
        throw new ArgumentOutOfRangeException("count",
            "count cannot be negative.");
    if (count == 0)
        return new T[0];

    var queue = new Queue<T>(count + 1);
    foreach (var item in source)
    {
        if (queue.Count == count)
            queue.Dequeue();
        queue.Enqueue(item);
    }
    return queue.AsEnumerable();
}
3
Timwi

初期容量のあるToListおよびToDictionary

基になるコレクションクラスの初期容量を公開するToListおよびToDictionaryオーバーロード。ソースの長さがわかっているか、制限されている場合に役立つことがあります。

public static List<TSource> ToList<TSource>(
    this IEnumerable<TSource> source, 
    int capacity)
{
    if (source == null)
    {
        throw new ArgumentNullException("source");
    }
    var list = new List<TSource>(capacity);
    list.AddRange(source);
    return list;
}     

public static Dictionary<TKey, TSource> ToDictionary<TSource, TKey>(
    this IEnumerable<TSource> source, 
    Func<TSource, TKey> keySelector, 
    int capacity,
    IEqualityComparer<TKey> comparer = null)
{
    return source.ToDictionary<TSource, TKey, TSource>(
                  keySelector, x => x, capacity, comparer);
}

public static Dictionary<TKey, TElement> ToDictionary<TSource, TKey, TElement>(
    this IEnumerable<TSource> source, 
    Func<TSource, TKey> keySelector, 
    Func<TSource, TElement> elementSelector,
    int capacity,
    IEqualityComparer<TKey> comparer = null)
{
    if (source == null)
    {
        throw new ArgumentNullException("source");
    }
    if (keySelector == null)
    {
        throw new ArgumentNullException("keySelector");
    }
    if (elementSelector == null)
    {
        throw new ArgumentNullException("elementSelector");
    }
    var dictionary = new Dictionary<TKey, TElement>(capacity, comparer);
    foreach (TSource local in source)
    {
        dictionary.Add(keySelector(local), elementSelector(local));
    }
    return dictionary;
}
3
Bear Monkey

重複

Aniの AssertCount メソッド(私はCountAtLeastと呼ばれるメソッドを使用します)のようなメソッドと組み合わせて使用​​すると、シーケンス内で複数回出現する要素を見つけるのが非常に簡単になります:

public static IEnumerable<T> Duplicates<T, TKey>(this IEnumerable<T> source,
    Func<T, TKey> keySelector = null, IEqualityComparer<TKey> comparer = null)
{
    source.ThrowIfNull("source");
    keySelector = keySelector ?? new Func<T, TKey>(x => x);
    comparer = comparer ?? EqualityComparer<TKey>.Default;

    return source.GroupBy(keySelector, comparer)
        .Where(g => g.CountAtLeast(2))
        .SelectMany(g => g);
}
3
Dan Tao

EnumerableEx.OfThis

public static class EnumerableEx
{
    public static IEnumerable<T> OfThese<T>(params T[] objects)
    {
        return objects;
    }
}

既知の値のシーケンスを簡単に作成するには:

var nums=EnumerableEx.OfThese(1,2,3);
var nums2 = nums.Concat(EnumerableEx.OfThese(4));
2
Nevermind

CountUpTo

static int CountUpTo<T>(this IEnumerable<T> source, int maxCount)
{
    if (maxCount == 0)
        return 0;

    var genericCollection = source as ICollection<T>; 
    if (genericCollection != null) 
        return Math.Min(maxCount, genericCollection.Count);

    var collection = source as ICollection; 
    if (collection != null)
        return Math.Min(maxCount, collection.Count);

    int count = 0;
    foreach (T item in source)
        if (++count >= maxCount)
            break;
    return count;
}
2
Gabe

合体

public static T Coalesce<T>(this IEnumerable<T> items) {
   return items.Where(x => x != null && !x.Equals(default(T))).FirstOrDefault();
   // return items.OfType<T>().FirstOrDefault(); // Gabe's take
}
2
mattmc3

ObjectWithMin/ObjectWithMax

TimwiのMinElementに似ていますが、Aggregate拡張メソッドを使用してソースのEnumerableをスピンするより簡潔なアルゴリズムがあります。

_public static T ObjectWithMax<T, TResult>(this IEnumerable<T> elements, Func<T, TResult> projection)
    where TResult : IComparable<TResult>
{
    if (elements == null) throw new ArgumentNullException("elements", "Sequence is null.");
    if (!elements.Any()) throw new ArgumentException("Sequence contains no elements.");

    //Set up the "seed" (current known maximum) to the first element
    var seed = elements.Select(t => new {Object = t, Projection = projection(t)}).First();

    //run through all other elements of the source, comparing the projection of each
    //to the current "seed" and replacing the seed as necessary. Last element wins ties.
    return elements.Skip(1).Aggregate(seed,
                              (s, x) =>
                              projection(x).CompareTo(s.Projection) >= 0
                                  ? new {Object = x, Projection = projection(x)}
                                  : s
        ).Object;
}

public static T ObjectWithMin<T, TResult>(this IEnumerable<T> elements, Func<T, TResult> projection)
    where TResult : IComparable<TResult>
{
    if (elements == null) throw new ArgumentNullException("elements", "Sequence is null.");
    if (!elements.Any()) throw new ArgumentException("Sequence contains no elements.");

    var seed = elements.Select(t => new {Object = t, Projection = projection(t)}).First();

    //ties won by the FIRST element in the Enumerable
    return elements.Aggregate(seed,
                              (s, x) =>
                              projection(x).CompareTo(s.Projection) < 0
                                  ? new {Object = x, Projection = projection(x)}
                                  : s
        ).Object;
}
_

これらのメソッドは、OrderBy().First()またはWhere(x=>x.SomeField == source.Min(someField)).First()と比較して非常に貴重であることがわかりました。これは線形であり、Enumerableを1回だけ通過し、リストの現在の順序に対して安定しています。

1
KeithS

GetBreadthFirstEnumerable

これは私のお気に入りです-どのコレクションでも幅優先探索を非常に簡単に実行できます。各反復で、メソッドは現在のノード、その親、グラフ内のレベル、および幅優先のインデックスを生成します。これは一部のシナリオでは非常に役立ちますが、必要がない場合は、必要に応じてメソッドを大幅に簡略化できます。ノード自体のみを生成できます。

public class IteratedNode<T>
{
    public T Node;
    public T ParentNode;
    public int Level;
    public int Index;
}

/// <summary>
/// Iterates over a tree/graph via In Order Breadth First search.
/// </summary>
/// <param name="root">The root item.</param>
/// <param name="childSelector">A func that receives a node in the tree and returns its children.</param>
public static IEnumerable<IteratedNode<T>> GetBreadthFirstEnumerable<T>(this T root, Func<T, IEnumerable<T>> childSelector)
{
    var rootNode = new IteratedNode<T> { Node = root, ParentNode = default(T), Level = 1, Index = 1};
    var nodesToProcess = new Queue<IteratedNode<T>>( new[] {rootNode});

    int itemsIterated = 0;
    while (nodesToProcess.Count > 0)
    {
        IteratedNode<T> currentItem = nodesToProcess.Dequeue();

        yield return currentItem; itemsIterated++;

        // Iterate over the children of this node, and add it to queue, to process later.
        foreach (T child in childSelector(currentItem.Node))
        {
            nodesToProcess.Enqueue( 
                new IteratedNode<T> {
                    Node = child,
                    ParentNode = currentItem.Node,
                    Level = currentItem.Level + 1,
                    Index = itemsIterated
                });                      
        }
    }
}
1
Omer Raviv

パラメータリストを除く

これは、特定のクエリをはるかに簡単にすることができる非常に単純なラッパーです。

public static IEnumerable<T> Except<T>(this IEnumerable<T> elements, params T[] exceptions)
{
    return elements.Except(exceptions);
}

使用法:

//returns a list of "work week" DayOfWeek values.
Enum.GetValues(typeof(DayOfWeek)).Except(DayOfWeek.Saturday, DayOfWeek.Sunday);

これにより、Exceptに渡すために独自のコレクションを作成したり、大きなブール値でWhereを使用したりする必要がなくなり、「Aと等しくなく、Bと...と等しくない」構造になり、コードが少しすっきりします。

述語を除く(逆の場所)

また、述語が与えられている場合を除いて、基本的にWhereの逆になっていることも確認しました。最も簡単な実装は、まさに次のとおりです。

public static IEnumerable<T> Except<T>(this IEnumerable<T> elements, Predicate<T> predicate)
{
   return elements.Where(x=>!predicate(x));
}

使用法:

//best use is as a "natural language" statement already containing Where()
ListOfRecords.Where(r=>r.SomeValue = 123).Except(r=>r.Id = 2536);

C#にdo-whileとdo-untilの両方の構造がある場合、これがその場所です。

1
KeithS

OneOrDefault

SingleOrDefaultと同様に、リストに複数の要素がある場合、例外をスローする代わりにnullを返します。

public static T OneOrDefault<T>(this IEnumerable<T> list)
{
    using (var e = list.GetEnumerator())
    {
        if (!e.MoveNext())
            return default(T);
        T val = e.Current;
        if (e.MoveNext())
            return default(T);
        return val;
    }
}
1
Gabe

私のはRemoveConcurrent<T>()です:

public static IEnumerable<T> RemoveConcurrent<T>(this IEnumerable<T> ts) where T : IEquatable<T>
{
    bool first = true;
    T lval = default(T);
    foreach (T t in ts)
    {
        if (first || !t.Equals(lval))
        {
            first = false;
            yield return t;
        }
        lval = t;
    }
}

標準のLINQクエリを使用してこれを行う方法があるかもしれませんが、そうであれば、方法がわかりません。

0
aboveyou00

の間に

よく知られているSQL「between」構造に相当するC#

/// <summary>
/// Determines if the current value is included in the range of specified values. Bounds are included.
/// </summary>
/// <typeparam name="T">The type of the values</typeparam>
/// <param name="val">The value.</param>
/// <param name="firstValue">The lower bound.</param>
/// <param name="secondValue">The upper bound.</param>
/// <returns>
/// Return <c>true</c> if the <paramref name="val">value</paramref> is between the <paramref name="firstValue"/> and the <paramref name="secondValue"/>; otherwise, <c>false</c>
/// </returns>
public static bool Between<T>(this T val, T firstValue, T secondValue) where T : IComparable<T>
{
  if (val == null)
    throw new ArgumentNullException();

  if (firstValue == null ||
      secondValue == null)
    return false;

  return firstValue.CompareTo(val) <= 0 && secondValue.CompareTo(val) >= 0;
}
0
Johann Blais
public static void AddRangeWhere<T>(this IEnumerable<T> list, IEnumerable<T> sourceList, Func<T, bool> predicate)
  {
   if (list != null)
   {
    List<T> newRange = new List<T>();

    foreach (var item in sourceList)
    {
     if (predicate(item))
      newRange.Add(item);
    }

    list.Concat(newRange);
   }
  }
0
John Egbert

パーティション

条件に基づいてIEnumerableを2つのIListに分割します

public static void Partition<T>(this IEnumerable<T> source, Func<T, bool> predicate, ref IList<T> matches, ref IList<T> nonMatches)
{
    if (source == null)
        throw new ArgumentNullException(nameof(source));
    if (predicate == null)
        throw new ArgumentNullException(nameof(source));

    var _matches = new List<T>();
    var _nonMatches = new List<T>();
    foreach (var itm in source)
    {
        if (predicate(itm))
            _matches.Add(itm);
        else
            _nonMatches.Add(itm);
    }

    if (matches == null)
        matches = new List<T>();
    else
        matches.Clear();
    if (nonMatches == null)
        nonMatches = new List<T>();
    else
        nonMatches.Clear();

    foreach (var m in _matches)
        matches.Add(m);
    nonMatches.Clear();
    foreach (var m in _nonMatches)
        nonMatches.Add(m);
    nonMatches = _nonMatches;
}
/// <summary>
/// Performs the specified action on each element of the IEnumerable.
/// </summary>
public static void ForEachAction<T>(this IEnumerable<T> enumerable, Action<T> action)
{
    if (action == null || enumerable == null)
    {
        throw new ArgumentNullException();
    }

    foreach (var item in enumerable)
    {
        action.Invoke(item);
    }
}
0
Salar

WhereLike

WhereとSQLLIKEパターンマッチングを組み合わせます。

public static IEnumerable<TSource> WhereLike<TSource>(this IEnumerable<TSource> source, Func<TSource, string> selector, string match)
    {
        /* Turn "off" all regular expression related syntax in
            * the pattern string. */
        string pattern = Regex.Escape(match);

        /* Replace the SQL LIKE wildcard metacharacters with the
        * equivalent regular expression metacharacters. */
        pattern = pattern.Replace("%", ".*?").Replace("_", ".");

        /* The previous call to Regex.Escape actually turned off
        * too many metacharacters, i.e. those which are recognized by
        * both the regular expression engine and the SQL LIKE
        * statement ([...] and [^...]). Those metacharacters have
        * to be manually unescaped here. */
        pattern = pattern.Replace(@"\[", "[").Replace(@"\]", "]").Replace(@"\^", "^");
        Regex reg = new Regex(pattern, RegexOptions.IgnoreCase);

        return source.Where(t => reg.IsMatch(selector(t)));
    }
0
dkackman