Ix.NETのススメ

# Ix.NETって何？

「Rx.NETやUniRxにあるXXXっていうメソッド（オペレーター）、なんでLINQにはないの？」って思った時におすすめなのがIx.NET。

以下、[README](https://github.com/dotnet/reactive)より

> The Interactive Extensions (Ix) is a .NET library which extends LINQ to Objects to provide many of the operators available in Rx but targeted for IEnumerable.

RxにあるオペレータをLINQ to Objectsに拡張した、つまり`IEnumerable<T>`で使えるようにしたのが、このライブラリです。

Ix.NETは、Rx.NETと同じリポジトリ([dotnet/reactive](https://github.com/dotnet/reactive))に存在し、同じメンバーがメンテナンスしています。

# Rxよくわかんないんだけど、使わなくていい？

Rx知らない人・使ったことがない人は、「Ix.NETはとっても便利なLINQのメソッドが、さらに増えた」と思ってください。

# Ix.NETのおすすめオペレーター

わかりやすいおすすめIxのオペレーターを紹介します。

## MaxByとMinBy

個人的に標準メソッド（LINQ）に入って欲しいメソッドナンバー1。

```csharp
public static IList<TSource> MaxBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
public static IList<TSource> MinBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
```

こんな`Monster`クラスがあって、

```csharp
public class Monster {
    public int Level { get; private set; } 
    /* 略 */
}
```

`List<Monster>`な`monsters`から、一番大きい`Level`を持つ`Monster`のオブジェクトを探したいです。

こういう時、実はLINQの`Max`メソッドは使えません。

```csharp
List<Monster> monsters = LoadMonsters();
int maxLevel = monsters.Max(it => it.Level);
```

LINQの`Max`メソッドでできるのは、「`monsters`の要素の中の、一番大きいLevelの値を求める」こと。

ここでやりたいのは「`monsters`の要素の中の、一番大きい`Level`の値をもつ要素を探すこと」こと。（それからもし最大値を複数の要素が持つなら、その全てを探したい。）

それができるのが、`MaxBy`。

```csharp
List<Monster> monsters = LoadMonsters();
IList<Monster> maxLevelMonsters = monsters.MaxBy(it => it.Level);
```

「要素の中で一番大きい評価値をもつ要素を探すこと」ことができるのがMaxBy。

逆に、

「要素の中で一番小さい評価値をもつ要素を探すこと」ことができるのがMinBy。

どちらも最大評価値・最小評価値が複数あった場合、その全てを返してくれます。

これ、本当に標準で使いたい・・・。

## IsEmpty

地味だけど、便利でわかりやすいメソッド。そしてなんでLINQに標準にないんだ系のメソッド。

要素が空かどうか判別するメソッド、IsEmpty。

[実装](https://github.com/dotnet/reactive/blob/master/Ix.NET/Source/System.Interactive/Empty.cs)はこんな感じ。`Any`を否定しているだけ。

```csharp
public static bool IsEmpty<TSource>(this IEnumerable<TSource> source)
{
    if (source == null)
    {
        throw new ArgumentNullException(nameof(source));
    }

    return !source.Any();
}
```

あ、それから`IEnumerable<T>`を空か判定するときは、間違っても`Count() == 0`はやっちゃいけいないよ。

https://qiita.com/RyotaMurohoshi/items/6f90a08f354032b6030d

## Buffer

数を指定して要素をまとめるメソッド、Bufferです。

```csharp
public static IEnumerable<IList<TSource>> Buffer<TSource>(this IEnumerable<TSource> source, int count)
```

```csharp
// [[0, 1], [2, 3], [4, 5]] みたいな感じでまとめられます。
IEnumerable<IList<int>> buffered = new List<int> { 0, 1, 2, 3, 4, 5 }.Buffer(count: 2);
```

余った要素は、余った分だけでまとめられます。

```csharp
// [[0, 1], [2, 3], [4, 5], [6]] みたいな感じでまとめられます。
IEnumerable<IList<int>> buffered = new List<int> { 0, 1, 2, 3, 4, 5, 6 }.Buffer(count: 2);
```

ずらす数、スキップ数を指定して、こんな感じにまとめることも。

```csharp
// [[0, 1, 2], [1, 2, 3], [2, 3, 4], [3, 4], [4]] みたいな感じでまとめられます。
IEnumerable<IList<int>> buffered = new List<int> { 0, 1, 2, 3, 4 }.Buffer(count: 3, skip: 1);
```

まとめる数よりスキップ数大きくすることもできます。


```csharp
// [[0, 1], [3, 4], [6, 7]] みたいな感じでまとめられます。
IEnumerable<IList<int>> buffered = new List<int> { 0, 1, 2, 3, 4, 5, 6, 7 }.Buffer(count: 2, skip: 3);
```

使いどころは多くはないけど、覚えておくと便利。

あと名前付き引数呼び出しで呼び出したいメソッド。

## Concat

これも地味に使いどころが多いメソッドです。他の言語だとflattenって呼ばれることもあります。

LINQにもともとあるConcatメソッドは、一つの`IEnumerable<T>`に他の`IEnumerable<T>`を連結するメソッド。

Ix.NETの`Concat`メソッドは、`IEnumerable<IEnumerable<T>>`が引数なしで呼び出すメソッド。

```csharp
public static IEnumerable<TSource> Concat<TSource>(this IEnumerable<IEnumerable<TSource>> sources)
```

```csharp
List<List<Monster>> listOfMonsterList = LoadListOfMonsterList();
IEnumerable<<Monster> monsters = listOfMonsterList.Concat();
```

`List<List<T>>`とか`Array<Array<T>>`とか、`IEnumerable<IList<T>>`とかが呼び出せます。

地味に便利！

## Repeat

LINQ標準のRepeat。「staticメソッドで、引数に渡した要素を引数の指定回数繰り返す`IEnumerable<T>`をつくる」ってメソッドです。実はプロダクトコードで使ったことは自分はまだありません。

一方で、Ix.NETの`Repeat`の各種オーバーロードは、「あー、これ使いたかったやつだ！」って自分はなりました。


```csharp
// 渡した要素を何回でも繰り返すIEnumerable<T>をつくる
public static IEnumerable<TResult> Repeat<TResult>(TResult value)

// IEnumerable<T>の拡張関数で、何回でも要素を繰り返すIEnumerable<T>をつくる
public static IEnumerable<TSource> Repeat<TSource>(this IEnumerable<TSource> source)

// IEnumerable<T>の拡張関数で、指定した回数繰り返すIEnumerable<T>をつくる
public static IEnumerable<TSource> Repeat<TSource>(this IEnumerable<TSource> source, int count)
```

## StartWith


`IEnumerable<T>`の拡張関数。可変長引数として渡した要素を、先頭につけて`IEnumerable<T>`を新たに生成。 

```csharp
public static IEnumerable<TSource> StartWith<TSource>(this IEnumerable<TSource> source, params TSource[] values)
```

## TakeLastとSkipLast

TakeとSkipのLast版。

ちなみに.NET Coreでは2.0からもう標準で使えます。.NET Standard 2.1で標準にはいったので、そのうち.NET Frameworkでも使えると思います。