LINQに追加されたメソッド

# 概要

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

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

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

* `Prepend`と`Append`
* `TakeLast`と`SkipLast`
* `ToHashSet`

# PrependとAppend

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

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

`List<T>`の[Add](https://docs.microsoft.com/en-us/dotnet/api/system.linq.enumerable.skiplast?view=netcore-2.2)や[Insert](https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic.list-1.insert?view=netcore-2.2)との違いに要注意！

## サンプルコード

`Prepend`。元の`IEnumerable<T>`を実装したオブジェクトに副作用を与えないことに注目。
 
```csharp
// 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>`を実装したオブジェクトに副作用を与えないことに注目。

```csharp
// 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
```

## シグニチャ

```csharp
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に入った](https://github.com/dotnet/standard/blob/master/docs/versions/netstandard1.6_diff.md)ので、上記環境以外にも.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](https://github.com/dotnet/standard/issues/514)

## 関連

* [Enumerable.Prepend Method(公式APIドキュメント)](https://docs.microsoft.com/en-us/dotnet/api/system.linq.enumerable.prepend?view=netcore-2.2)
* [Enumerable.Append Method(公式APIドキュメント)](https://docs.microsoft.com/en-us/dotnet/api/system.linq.enumerable.append?view=netcore-2.2)
* [.NET Standard 1.6 vs. 1.5](https://github.com/dotnet/standard/blob/master/docs/versions/netstandard1.6_diff.md)
* [.NET Framework 4.7.1 provides built-in support for .NET Standard 2.0](https://github.com/dotnet/standard/issues/514) (dotnet/standardのissue)

# ToHashSet

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

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

重複を取り除くという観点では似たようなメソッドに、[Distinct](https://docs.microsoft.com/en-us/dotnet/api/system.linq.enumerable.distinct?view=netcore-2.2)があります。


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


## シグニチャ

```csharp
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に入った](https://github.com/dotnet/standard/blob/master/docs/versions/netstandard2.1_diff.md)ので、上記環境以外にも.NET Standardを準拠する環境で将来的に入る見込みですね。

## 関連

* [Enumerable.ToHashSet Method(公式APIドキュメント)](https://docs.microsoft.com/en-us/dotnet/api/system.linq.enumerable.tohashset?view=netframework-4.7.2)
* [Announcing the .NET Framework 4.7.2](https://blogs.msdn.microsoft.com/dotnet/2018/04/30/announcing-the-net-framework-4-7-2/)の「BCL – Additional Collection APIs」
* [Announcing .NET Standard 2.1](https://blogs.msdn.microsoft.com/dotnet/2018/11/05/announcing-net-standard-2-1/)
* [.NET Standard 2.1 vs. 2.0](https://github.com/dotnet/standard/blob/master/docs/versions/netstandard2.1_diff.md)

# TakeLastとSkipLast

まずは、`TakeLast`。

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

[TakeLastの公式ドキュメント](https://docs.microsoft.com/en-us/dotnet/api/system.linq.enumerable.takelast?view=netcore-2.2)より

> 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の公式ドキュメント](https://docs.microsoft.com/en-us/dotnet/api/system.linq.enumerable.skiplast?view=netcore-2.2)より

> 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>`

## シグニチャ

```csharp
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に入った](https://github.com/dotnet/standard/blob/master/docs/versions/netstandard2.1_diff.md)ので、上記環境以外にも.NET Standardに準拠した環境で将来的に入る見込みですね。

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

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

## 関連

* [Enumerable.TakeLast Method(公式APIドキュメント)](https://docs.microsoft.com/en-us/dotnet/api/system.linq.enumerable.takelast?view=netcore-2.2)
* [Enumerable.SkipLast Method(公式APIドキュメント)](https://docs.microsoft.com/en-us/dotnet/api/system.linq.enumerable.skiplast?view=netcore-2.2)
* [Announcing the .NET Framework 4.7.2](https://blogs.msdn.microsoft.com/dotnet/2018/04/30/announcing-the-net-framework-4-7-2/)の「BCL – Additional Collection APIs」
* [Announcing .NET Standard 2.1](https://blogs.msdn.microsoft.com/dotnet/2018/11/05/announcing-net-standard-2-1/)
* [dotnet/reactive(Ix.NETを含むGitHubリポジトリ)](https://github.com/dotnet/reactive)
* [.NET Standard 2.1 vs. 2.0](https://github.com/dotnet/standard/blob/master/docs/versions/netstandard2.1_diff.md)


# Zip

今までは、

```csharp
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>`)」が必要。匿名型とかと組み合わせることも多い

```csharp
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に。分解とのコンボも！短くてスッキリ！

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


## シグニチャ

```csharp
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](https://github.com/RyotaMurohoshi/ImportedLinq)にもあるので、本番環境で今すぐ使いたい人はそちらを使ってみてください。

## 関連

* [Enumerable.Zip Method(公式APIドキュメント)](https://docs.microsoft.com/en-us/dotnet/api/system.linq.enumerable.zip?view=netcore-3.0#System_Linq_Enumerable_Zip__2_System_Collections_Generic_IEnumerable___0__System_Collections_Generic_IEnumerable___1__)


Corefxの関連 Issue

* https://github.com/dotnet/corefx/issues/16011
* https://github.com/dotnet/corefx/issues/24788

Corefxの関連 PullRequest

* https://github.com/dotnet/corefx/pull/26582
* https://github.com/dotnet/corefx/pull/33709
* https://github.com/dotnet/corefx/pull/35595