LoginSignup
34
23

More than 3 years have passed since last update.

LINQに追加されたメソッド

Last updated at Posted at 2019-01-03

概要

NET Framework 3.5でLINQが実装され、NET Framework 4.0でZipメソッドが追加されました。

それ以降、なかなかメソッドは増えていなかったのですが、最近になっていくつかLINQ to Objectsにメソッドが追加されています。

この投稿ではそれらを紹介します。

  • PrependAppend
  • TakeLastSkipLast
  • ToHashSet

PrependとAppend

  • Prepend : 先頭に要素を追加した新しいIEnumerable<T>をつくる
  • Append : 末尾に要素を追加した新しいIEnumerable<T>をつくる

PrependAppendは、元のIEnumerable<T>を実装したオブジェクトの中身を変更しません。

List<T>AddInsertとの違いに要注意!

サンプルコード

Prepend。元のIEnumerable<T>を実装したオブジェクトに副作用を与えないことに注目。

// Creating a list of numbers
List<int> numbers = new List<int> { 1, 2, 3, 4 };

// Trying to prepend any value of the same type
numbers.Prepend(0);

// It doesn't work because the original list has not been changed
Console.WriteLine(string.Join(", ", numbers));

// It works now because we are using a changed copy of the original list
Console.WriteLine(string.Join(", ", numbers.Prepend(0)));

// If you prefer, you can create a new list explicitly
List<int> newNumbers = numbers.Prepend(0).ToList();

// And then write to the console output
Console.WriteLine(string.Join(", ", newNumbers));

// This code produces the following output:
//
// 1, 2, 3, 4
// 0, 1, 2, 3, 4
// 0, 1, 2, 3, 4

Append。こっちも、元のIEnumerable<T>を実装したオブジェクトに副作用を与えないことに注目。

// Creating a list of numbers
List<int> numbers = new List<int> { 1, 2, 3, 4 };

// Trying to append any value of the same type
numbers.Append(5);

// It doesn't work because the original list has not been changed
Console.WriteLine(string.Join(", ", numbers));

// It works now because we are using a changed copy of the original list
Console.WriteLine(string.Join(", ", numbers.Append(5)));

// If you prefer, you can create a new list explicitly
List<int> newNumbers = numbers.Append(5).ToList();

// And then write to the console output
Console.WriteLine(string.Join(", ", newNumbers));

// This code produces the following output:
//
// 1, 2, 3, 4
// 1, 2, 3, 4, 5
// 1, 2, 3, 4, 5

シグニチャ

public static IEnumerable<TSource> Append<TSource>(this IEnumerable<TSource> source, TSource element);
public static IEnumerable<TSource> Prepend<TSource>(this IEnumerable<TSource> source, TSource element);

利用できる環境

  • .NET Core 1.0~
  • .NET Framewprk 4.7.1~

ちなみにこのメソッドは、.NET Standard 1.6に入ったので、上記環境以外にも.NET Standardを準拠する環境ですでに入っています。

TODO 確認と動作検証

.NET Framework 4.6.1は、.NET Standard 1.6に準拠しています。
サポートファイルを使えば、Append・Prependは使える可能性あり。
一方、公式ドキュメントには記載はありません。
要動作検証
参考 .NET Framework 4.7.1 provides built-in support for .NET Standard 2.0

関連

ToHashSet

名前から想像できる通り、ToListToDictionaryHashSet版。IEnumerable<T>からHashSet<T>を作成するメソッドです。

重複要素を取り除いて、HashSet<T>を生成したい時に使うメソッドです。

重複を取り除くという観点では似たようなメソッドに、Distinctがあります。

  • ToHashSetToListToDictionaryと同様に、メソッド呼び出しと同時に全ての要素が列挙されてコレクションが生成される
  • Distinctは、SelectWhereと同様に、メソッド呼び出した時に要素の列挙はされない。Distinctの返り値のIEnumerable<T>が列挙された時に、重複排除の内部処理が走る。

シグニチャ

public static HashSet<TSource> ToHashSet<TSource>(this IEnumerable<TSource> source);
public static HashSet<TSource> ToHashSet<TSource>(this IEnumerable<TSource> source, IEqualityComparer<TSource> comparer);

利用できる環境

  • .NET Core 2.0~
  • .NET Framewprk 4.7.2~

ちなみにこのメソッドは、.NET Standard 2.1に入ったので、上記環境以外にも.NET Standardを準拠する環境で将来的に入る見込みですね。

関連

TakeLastとSkipLast

まずは、TakeLast

Takeの末尾からやる版。指定個数countを渡し、元のIEnumerable<T>から末尾から指定個数個だけ要素をピックアップしたIEnumerable<T>を生成するメソッドです。

TakeLastの公式ドキュメントより

Returns a specified number of contiguous elements from the end of a sequence.

Takeと同様に

  • 引数のcountが0以下の時は、例外が発生したりせず、空のIEnumerable<T>
  • 引数のcountがIEnumerable<T>の長さより大きい場合は、例外が発生したりせず、全ての要素がピックされたIEnumerable<T>

次は、SkipLast.

Skipの末尾からやる版。指定個数countを渡し、元のIEnumerable<T>から、末尾から指定個数個だけ取り除いたIEnumerable<T>を生成するメソッドです。

SkipLastの公式ドキュメントより

Omits a specified number of elements from the end of a sequence and returns the remaining elements.

Skipと同様に

  • 引数のcountが0以下の時、例外が発生したりせず、何も要素が取り除かれていないIEnumerable<T>
  • 引数のcountがIEnumerable<T>の長さより大きい場合は、例外が発生したりせず、空のIEnumerable<T>

シグニチャ

public static IEnumerable<TSource> SkipLast<TSource>(this IEnumerable<TSource> source, int count);
public static IEnumerable<TSource> TakeLast<TSource>(this IEnumerable<TSource> source, int count);

利用できる環境

  • .NET Core 2.0~

ちなみにこのメソッドは、.NET Standard 2.1に入ったので、上記環境以外にも.NET Standardに準拠した環境で将来的に入る見込みですね。

そのうち、.NET Frameworkでも使えるようになると思います。。

あと、Ix.NETにも同様なメソッドが存在します。.NET Core以外で今すぐ使いたいのであれば、Ix.NETを導入するのもありかもしれません。

関連

Zip

今までは、

public static IEnumerable<TResult> Zip<TFirst,TSecond,TResult> (this IEnumerable<TFirst> first, IEnumerable<TSecond> second, Func<TFirst,TSecond,TResult> resultSelector);
  • シーケンス(IEnumerable<TFirst>)
  • シーケンス(IEnumerable<TFirst>)
  • 合成するためのデリゲート(Func<TFirst, TSecond, TResult>)

を引数にとって、

  • シーケンス(IEnumerable<TResult>)

を返すメソッドしかありませんでした。

これに新しいオーバーロードが追加されました。

新しいオーバーロードは、「合成するデリゲート」が必要ないものです。

  • シーケンス(IEnumerable<TFirst>)
  • シーケンス(IEnumerable<TFirst>)

を引数に、

  • シーケンス(IEnumerable<ValueTuple<TFirst, TSecond>)

を返すものです。

嬉しい!

Zipする時って、「とりあえずまとめて後続の処理につなげる」ことも多いので、そのシーンでわざわざ「合成するためのデリゲート(Func<TFirst, TSecond, TResult>)」を用意する必要がないのがうれしい!

今までは↓こんな感じで、「合成するためのデリゲート(Func<TFirst, TSecond, TResult>)」が必要。匿名型とかと組み合わせることも多い

foreach(var element in firstSequence.Zip(secondSequence, (first, second) => new {first, second})){
    var first = element.first;
    var second = element.second;
    Console.WriteLine(first + " " + second);
}

今までは↓こんな感じで、ZipしてValueTupeに。分解とのコンボも!短くてスッキリ!

foreach(var (first, second) in firstSequence.Zip(secondSequence)){
    Console.WriteLine(first + " " + second);
}

シグニチャ

public static IEnumerable<ValueTuple<TFirst,TSecond>> Zip<TFirst,TSecond> (this IEnumerable<TFirst> first, IEnumerable<TSecond> second);

利用できる環境

  • .NET Core 3.0

.NET Core 3.0は現在Previewの段階です。

Zipメソッドは、Preview5から使えるようになりました。

同様のメソッドが拙作のImportedLinqにもあるので、本番環境で今すぐ使いたい人はそちらを使ってみてください。

関連

Corefxの関連 Issue

Corefxの関連 PullRequest

34
23
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
34
23