Ix.NETって何?
「Rx.NETやUniRxにあるXXXっていうメソッド(オペレーター)、なんでLINQにはないの?」って思った時におすすめなのがIx.NET。
以下、READMEより
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)に存在し、同じメンバーがメンテナンスしています。
Rxよくわかんないんだけど、使わなくていい?
Rx知らない人・使ったことがない人は、「Ix.NETはとっても便利なLINQのメソッドが、さらに増えた」と思ってください。
Ix.NETのおすすめオペレーター
わかりやすいおすすめIxのオペレーターを紹介します。
MaxByとMinBy
個人的に標準メソッド(LINQ)に入って欲しいメソッドナンバー1。
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
クラスがあって、
public class Monster {
public int Level { get; private set; }
/* 略 */
}
List<Monster>
なmonsters
から、一番大きいLevel
を持つMonster
のオブジェクトを探したいです。
こういう時、実はLINQのMax
メソッドは使えません。
List<Monster> monsters = LoadMonsters();
int maxLevel = monsters.Max(it => it.Level);
LINQのMax
メソッドでできるのは、「monsters
の要素の中の、一番大きいLevelの値を求める」こと。
ここでやりたいのは「monsters
の要素の中の、一番大きいLevel
の値をもつ要素を探すこと」こと。(それからもし最大値を複数の要素が持つなら、その全てを探したい。)
それができるのが、MaxBy
。
List<Monster> monsters = LoadMonsters();
IList<Monster> maxLevelMonsters = monsters.MaxBy(it => it.Level);
「要素の中で一番大きい評価値をもつ要素を探すこと」ことができるのがMaxBy。
逆に、
「要素の中で一番小さい評価値をもつ要素を探すこと」ことができるのがMinBy。
どちらも最大評価値・最小評価値が複数あった場合、その全てを返してくれます。
これ、本当に標準で使いたい・・・。
IsEmpty
地味だけど、便利でわかりやすいメソッド。そしてなんでLINQに標準にないんだ系のメソッド。
要素が空かどうか判別するメソッド、IsEmpty。
実装はこんな感じ。Any
を否定しているだけ。
public static bool IsEmpty<TSource>(this IEnumerable<TSource> source)
{
if (source == null)
{
throw new ArgumentNullException(nameof(source));
}
return !source.Any();
}
あ、それからIEnumerable<T>
を空か判定するときは、間違ってもCount() == 0
はやっちゃいけいないよ。
Buffer
数を指定して要素をまとめるメソッド、Bufferです。
public static IEnumerable<IList<TSource>> Buffer<TSource>(this IEnumerable<TSource> source, int count)
// [[0, 1], [2, 3], [4, 5]] みたいな感じでまとめられます。
IEnumerable<IList<int>> buffered = new List<int> { 0, 1, 2, 3, 4, 5 }.Buffer(count: 2);
余った要素は、余った分だけでまとめられます。
// [[0, 1], [2, 3], [4, 5], [6]] みたいな感じでまとめられます。
IEnumerable<IList<int>> buffered = new List<int> { 0, 1, 2, 3, 4, 5, 6 }.Buffer(count: 2);
ずらす数、スキップ数を指定して、こんな感じにまとめることも。
// [[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);
まとめる数よりスキップ数大きくすることもできます。
// [[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>>
が引数なしで呼び出すメソッド。
public static IEnumerable<TSource> Concat<TSource>(this IEnumerable<IEnumerable<TSource>> sources)
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
の各種オーバーロードは、「あー、これ使いたかったやつだ!」って自分はなりました。
// 渡した要素を何回でも繰り返す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>
を新たに生成。
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でも使えると思います。