Enumalableクラスのメソッドについて本気出して調べてみた

# まえがき

久しぶりの投稿です。しばらく何も書いてなかったのは、~~やる気が出なかった~~お仕事が忙しかったからです。

`Select`に関して書こうと思ったけど~~面倒くさくなって~~書くことがなく、とりあえずLINQ全体についてダラダラ書こうと思ってたら、なんだか長くなってしまいました。

定期的に見直して、書き直す予定です。~~(予定は未定)~~

## LINQですよ、LINQ！

System.Core.dllの`System.Linq`名前空間の `Enumalable`クラスにあるメソッドの解説を並び立てる記事です。対象 は .Net Framework 4.7.2です。なので、`SkipLast`と`TakeLast`は対象外です。

`SkipLast`

https://docs.microsoft.com/ja-jp/dotnet/api/system.linq.enumerable.skiplast

`TakeLast`

https://docs.microsoft.com/ja-jp/dotnet/api/system.linq.enumerable.takelast

## 今回説明に使うデータ

アイドルマスターです。わっほ～い


``` C#
// アイドルの名前だけを読み取り専用で提供するインターフェース
interface IIDOLName
{
    string Name { get; }
}

// アイドルちゃん
class IDOL : IIDOLName
{
    // 名前
    public string Name { get; set; }
    // 読み仮名
    public string Phonetic { get; set; }
    // 年齢
    public int Age { get; set; }
    // 身長
    public double Height { get; set; }
    // 体重
    public double Weight { get; set; }
    // バスト
    public double Bust { get; set; }
    // ウエスト
    public double Waist { get; set; }
    // ヒップ
    public double Hip { get; set; }
    // 作品ID
    public int WorkID { get; set; }
    // 所属しているユニットの一覧
    public List<string> Unit { get; set; }
}
// アイマスキャラ277人分のデータが入ってます。(アイドル+トレーナー+事務員)
// 公開されてないパラメータは0埋めです。
List<IDOL> IDOLList;
```

# 分類

独断と偏見で10種類に分類しました。

- 射影系
- フィルタ系
- 探索系
- 算術系
- 単体取得系
- 変換系
- ソート系
- 範囲取得系
- 結合系
- その他

# 射影系

とりあえずMicrosoftの公式ドキュメントから説明を引っ張ってきました。

>  射影操作 (C#) 
>
>  射影とは、オブジェクトを、必要なプロパティだけで構成された別の形式に変換する操作のことをいいます。 

つまり、「もとのデータ」を「今使いたいデータ」に変換して取り出すメソッド達です。

## Select

```c#
// 特定のプロパティを取り出したり
var bust = IDOLList.Select(x => x.Bust);
// 計算した結果を取り出したり
var bmi = IDOLList.Select(x => x.Weight / x.Height / x.Height);
// タプルで取り出したり
var namePhone = IDOLList.Select(x => (x.Name, x.Phonetic));
// ラムダ式じゃなくても大丈夫
var nonelambdaName = IDOLList.Select(GetName);
// インデックス付きのSelect
var indexIdol = IDOLList.Select((x,i) => (i, x));
// もとのデータ完全を無視も一応できる(ムダだけど)
var muda = IDOLList.Select(x => "0");

string GetName(IDOL x)
{
    return x.Name;
}
```

射影系の中でも一番シンプルなやつです。そして、シンプルだけに一番人気候補です。

Microsoftも分かってるようで、`Select`と、もう一つの一番人気候補`Where`に関してはパフォーマンスのための最適化がかなりかかっています。

## SelectMany

```c#
// 所属しているユニットが順番に結合される
var fullUnit = IDOLList.SelectMany(x => x.Unit);
// 名前に使われてる文字をすべて引っ張りだす
var nameChar = IDOLList.SelectMany(x => x.Name.ToList());
```

少しわかりにくいメソッドですが、やっている事はそんなに難しくないです。

もとのデータから新たな`IEnumerable`を取り出し、それを1つに結合します。

## Cast

```c#
// IIDOLNameインターフェイスにキャストされる
var castIF = IDOLList.Cast<IIDOLName>();
```

射影系なのかどうかと聞かれると、よくわかりませんが、他に入れるところも思いつかなかったので入れちゃいました。

各データに対して、ジェネリックで渡された方にキャストします。が、後述の`OfType`と異なり**キャストに失敗すると例外を吐く**ので、あまり使われてない気がするメソッドです。

# フィルタ系

読んで字のごとく、フィルタリングするメソッド達です。

## Where

```c#
// 特定のプロパティでフィルタリングしたり
var higher = IDOLList.Where(x => x.Height > 160);
// 計算結果でフィルタリングしたり
var noData = IDOLList.Where(x => x.Height * x.Weight == 0);
// ラムダ式じゃなくても大丈夫
var nonelambdaAmami = IDOLList.Where(IsHaruka);
// インデックス付きのWhere
var oddIdol = IDOLList.Where((x,i) => i % 2 == 0);
// もとのデータ完全を無視も一応できる(ムダだけど)
var muda = IDOLList.Where(x => true);

string IsHaruka(IDOL x)
{
    return x.Name == "天海 春香";
}
```

フィルタ系の基本です。データリストに対して何か処理をする前に不都合なデータを弾くために使うことが多いと思います。

```c#
// 身長が0だと例外が発生するので
var bmiException = IDOLList.Select(x => x.Weight / x.Height / x.Height);
// 事前に取り除くことで対応する
var bmi = IDOLList.Where(x => x.Height != 0).Select(x => x.Weight / x.Height / x.Height);
```

こいつもかなり最適化されているので、興味がある人は[昔書いたやつ](<https://qiita.com/NCT48/items/9a4634fa58806d0cd5be>)を読んでみてください。

## OfType

```c#
// IIDOLNameインターフェイスにキャストされる
var castIF = IDOLList.OfType<IIDOLName>();
```

各データに対して、ジェネリックで渡された方にキャストします。が、前述の`Cast`と異なり**キャストに失敗した要素はスキップされる**ので、使い勝手が良いメソッドです。

メソッドの実行後に全体のデータ数が減るので、フィルタ系ってことになってます(僕の中では)。

## Distinct

```c#
// 特に指定しなければ規定の比較
var uniqueNameChar = IDOLList.SelectMany(x => x.Name.ToList()).Distinct();
// 自作の比較を指定することも可能(年齢の被りなしで取り出す)
var originalCompare = IDOLList.Distinct(new IdolCompare());

class IdolCompare : IEqualityComparer<IDOL>
{
    public bool Equals(IDOL x, IDOL y)
    {
        return x.Age == y.Age;
    }

    public int GetHashCode(IDOL obj)
    {
        return obj.Age.GetHashCode();
    }
}
```

重複削除メソッドです。組み込み型だったら特に何も考えなくて良いんですが、自作クラスの重複削除だとちょっと面倒です。

1. 自作クラスに`IEquatable`インターフェイスを実装する。
2. 外部から`IEqualityComparer`インターフェイスを渡す。

1の方が実装が1つにまとまるので良いと思います( [こんな感じ](<https://qiita.com/Chrowa3/items/51e7033aa687c6274ad4>) )が、必ずしも自分で弄れるクラスばかりではないので2の出番もあるかと思います。(他にも方法はあるんですかね？僕は知りません。)

※追記(コメントにて頂いたアドバイス)

``` c#
// アイドルちゃんを少し改造
class IDOL : IIDOLName
{
    /*
        既存プロパティ類は省略
    */
    
    // IEqualityComparerを渡す読み取り専用プロパティ
    public static IEqualityComparer<IDOL> ComparerName => new EqualityrName();
    public static IEqualityComparer<IDOL> ComparerAge => new EqualityrAge();
    
    // IEqualityComparerを継承しているクラス
    class EqualityrName : IEqualityComparer<IDOL>
    {
        public bool Equals(IDOL x, IDOL y)
        {
            return x.Name == y.Name;
        }

        public int GetHashCode(IDOL obj)
        {
            return obj.Name.GetHashCode();
        }
    }
    class EqualityrAge : IEqualityComparer<IDOL>
    {
        public bool Equals(IDOL x, IDOL y)
        {
            return x.Age == y.Age;
        }

        public int GetHashCode(IDOL obj)
        {
            return obj.Age.GetHashCode();
        }
    }
}
// 年齢で比較可能
var compareAge = IDOLList.Distinct(IDOL.ComparerAge);
```

「`IEqualityComparer`を提供するプロパティ」と「`IEqualityComparer`を継承しているクラス」を元のクラス内に定義すると、実装を1つにまとめつつ、複数の比較方法を使い分けられます。(これ以降のメソッドも同様です。)

また、タプルや匿名型等の比較を行いたい場合、`IEquatable`インターフェイスを実装できないので、`IEqualityComparer`を使用する事になります。

# 判定系

指定した条件を満たしていたら`ture`、そうでなければ`false`を返すメソッド達です。

## Any

```c#
// 条件を満たしているので、true
var isOppai = IDOLList.Any(x => x.Bust >= 100);
// データが有るのでtrue
var anyData = IDOLList.Any();
```

データリスト内に、指定した条件を満たすものが1つでもあれば`true`、1つもなければ`false`です。

条件を指定しない場合、何でも良いから要素が1つでもあれば`true`です。

`Count() == 0`とかやっちゃうとLINQ警察が飛んできて、こいつを投げつけてきます。

## All

```c#
// 条件を満たしていないので、false
var isOppai = IDOLList.All(x => x.Bust >= 100);
// これは存在しない
//var allData = IDOLList.All();
```

データリスト内の、すべてが指定の条件を満たすせば`true`、1つでも満たさなければ`false`です。

`Any`と違って条件なしはありません。「なんでも良いから要素が全て入っている」→意味がわからないので。

## Contains

``` c#
var blankIDOL = new IDOL();
IDOLList.Add(blankIDOL);
// 同一のインスタンスなのでture
var isBlank1 = IDOLList.Contains(blankIDOL);
// 別のインスタンスなのでfalse
var isBlank2 = IDOLList.Contains(new IDOL());
// IEqualityComparerが指定されているので、名前で比較できる。よって、true
var isBlank3 = IDOLList.Contains(new IDOL(), new IdolCompare());

class IdolCompare : IEqualityComparer<IDOL>
{
    public bool Equals(IDOL x, IDOL y)
    {
        return x.Name == y.Name;
    }

    public int GetHashCode(IDOL obj)
    {
        return obj.Name.GetHashCode();
    }
}
```

データリスト内に、引数で渡したデータと同一の物があれば、`true`、なければ`false`です。

`Distinct`と同様で、自作クラスの判定だとちょっと面倒です。`isBlank1`と`isBlank3`は`true`ですが、`isBlank2`は`false`になります。

基本的には`Any`の方が使い勝手が良いですが、ラムダ式の中で呼ぶ時にこっちを使うことがあります。

``` c#
// ラムダ式の中にラムダ式だと視認性が良くない
var newGenerationsAny = IDOLList.Where(x => x.Unit.Any(y => y == "ニュージェネレーション"));
// わりとスッキリ
var newGenerationsContains = IDOLList.Where(x => x.Unit.Contains("ニュージェネレーション"));
```



## SequenceEqual

``` c#
// IDOLListには、渋谷 凛、神谷 奈緒、北条 加蓮の順に入っているものとする。
var triadPrimus =  IDOLList.Where(x => x.Unit.Contains("トライアドプリムス"));
var siburin = new IDOL{Name = "渋谷 凛"};
var kamiyan = new IDOL{Name = "神谷 奈緒"};
var karen = new IDOL{Name = "北条 加蓮"};
var torapuri = new List<IDOL>
    siburin,
    kamiyan,
    karen,
};
// ２つのシーケンスが等しいか、順番に確認
// こっちはfalse(中身が別のインスタンス)
var isTriadPrimus1 = triadPrimus.SequenceEqual(torapuri);
// こっちはtrue(IEqualityComparerによりNameで比較される)
var isTriadPrimus2 = triadPrimus.SequenceEqual(torapuri, new IdolCompare());

class IdolCompare : IEqualityComparer<IDOL>
{
    public bool Equals(IDOL x, IDOL y)
    {
        return x.Name == y.Name;
    }

    public int GetHashCode(IDOL obj)
    {
        return obj.Name.GetHashCode();
    }
}
```

今までのとは少し違います。2つのデータリストに対して要素を順番に比較し、全て一致していたら`true`、そうでなければ`false`です。

意外と使い勝手の良いメソッドで、2つのデータリストの型が違っても比較可能です。(今回の例だと`IEnumarable`と`List`。)

ただし、自作クラスの判定だとやっぱりちょっと面倒です。

# 算術系

いろいろな計算をしてくれるメソッド達です(適当)。

## Average

``` c#
// 単純な平均を求める
// 17.1でした
var ageAve = IDOLList.Average(x => x.Age);
// フィルタリングしてからやってもよし
// 80.9でした
var bustAve = IDOLList.Where(x => x.Bust != 0).Average(x => x.Bust);
```

平均値を求めます。

こいつに限った話でないですが、算術系は大量のオーバーロードがあります。(`Average`だけで20個)

とは言っても、

- `int`
- `long`
- `float`
- `double`
- `decimal`

これら5種類の変数と、それぞれのnull許容型(?がつくやつ)、およびその両方のラムダ式に対応したメソッドなので、特にビビる必要はないです。

## Sum

``` c#
// 単純な合計を求める
// 43098.2でした
var heightSum = IDOLList.Sum(x => x.Height);
// 計算してからやってもよし
// 6649でした
var aboutUnderBustSum = IDOLList.Select(x => x.Bust - x.Waist).Sum();
```

合計を求めます。

こいつもオーバーロードが20個あります。内訳は`Average`と同様です。

## Max

``` c#
// 最大値を求める
// 93でした
var hipMax = IDOLList.Max(x => x.Hip);
// フィルタリングしてからやってもよし
// 86でした
var undeJCBust = IDOLList.Where(x => x.Age < 16).Max(x => x.Bust);
```

最大値を求めます。

こいつはオーバーロードが**22個**あります。内20個は、`Average`と同様です。

```c#
// これでは年齢しか取れない
var maxAge = IDOLList.Max(x => x.Age);
// 特に何もしない場合、例外が発生
var max = IDOLList.Max();
// 最高齢の人の名前と年齢が同時に取れる
var maxAgeIDOL = IDOLList.Select(x => new IDOLAge(x)).Max();
// 高橋 礼子,31
Console.WriteLine($"{d.Name},{d.Age}");

// 比較用のクラスを作成
class IDOLAge : IComparable<IDOLAge>
{
    public string Name{ get; set; } 
    public int Age{ get; set; } 
    public IDOLAge(IDOL x)
    {
        Name = x.Name;
        Age = x.Age;
    }
    
    public int CompareTo(IDOLAge other)
    {
        return Age - other.Age;
    }
}
```

残り2つの内の1つです。(もう1つはラムダ式を用いて、これと同じものを作成するメソッド)

`Average`で上げた5種類の変数以外のリストでも、`Max`と`Min`は使用可能です。ただし、そのままでは実行時に例外を吐いて死にます。

**データリストの中身が`IComparable`か`IComparable<T>`を継承している**場合、比較が可能です。

一番大きい値を取得しつつ、他のパラメータも参照したい時にこの方法が使えます。

## Min

``` c#
// 最小値を求める
// 0でした(データが入ってない人がいるので)
var waistMin = IDOLList.Min(x => x.Waist);
// フィルタリングして計算してからやってもよし
// 14.9でした
var bmiMin = IDOLList.Where(x => x.Weight * x.Height != 0).Select(x => x.Weight / x.Height / x.Height).Min();
```

最小値を求めます。

こいつもオーバーロードが**22個**あります。内訳は`Max`と同様です。

## Count, LongCount

``` c#
// 要素数の合計を求める
var idolCount = IDOLList.Count();
// 条件の指定も可能
var jsCount = IDOLList.Count(x => x.Age < 13);

// 要素数を求める
var idolLongCount = IDOLList.LongCount();
// 条件の指定も可能
var jkLongCount = IDOLList.LongCount(x => 15 < x.Age && x.Age < 19);
```

要素数の合計を求めます。条件を指定することも可能です。

2つともほぼ同じなので、基本は`Count`で十分です。その名の通り`LongCount`は戻りが`Long`なので、アホみたいに大きなデータの塊を扱う場合のみこっちを使ってください。

## Aggregate

``` c#
// 年齢の合計を求める→Sum
var sumAge = IDOLList.Select(x = >x.Age).Aggregate((x, y) => x + y);
// 要素数を求める→Count
var countIdol = IDOLList.Aggregate(0, (x, y) => x + 1);
// 身長の平均を求める→Average
var aveHeight = IDOLList.Select(x => x.Height)
                        .Aggregate(
                          (height: 0.0, count: 0), 
                          (x, y) => x = (x.height + y, x.count + 1), 
                          z => z.height / z.count);
```

今まで出てきた算術系メソッドは(理論上)こいつですべて置き換え可能(なはず)です。~~ただし、面倒。~~

少し複雑なので、しっかり解説したいと思います。

オーバーロードは全部で3つです。(上の3つの例と対応してます。)

``` c#
TSource Aggregate<TSource>(this IEnumerable<TSource> source, Func<TSource, TSource, TSource> func)

TAccumulate Aggregate<TSource, TAccumulate>(this IEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate, TSource, TAccumulate> func)

TResult Aggregate<TSource, TAccumulate, TResult>(this IEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate, TSource, TAccumulate> func, Func<TAccumulate, TResult> resultSelector)
```

### その1

まずは1つ目から。

``` c#
// 年齢を取り出して合計を求める
var ageSum = IDOLList.Select(x = >x.Age).Aggregate((x, y) => x + y);
// 中身はこんな感じです
public static TSource Aggregate<TSource>(this IEnumerable<TSource> source, Func<TSource, TSource, TSource> func)
{
	if (source == null)
	{
		throw Error.ArgumentNull("source");
	}
	if (func == null)
	{
		throw Error.ArgumentNull("func");
	}
	using (IEnumerator<TSource> enumerator = source.GetEnumerator())
	{
		if (!enumerator.MoveNext())
		{
			throw Error.NoElements();
		}
        // 初期値として1つ目のデータを設定
		TSource val = enumerator.Current;
		while (enumerator.MoveNext())
		{
            // 今回の場合、valがx,enumerator.Currentがyにあたる
			val = func(val, enumerator.Current);
		}
		return val;
	}
}
```

`Aggregate`の実装内容を引っ張り出してきました。

**「`Aggregate`の引数で指定するラムダ式」**は**「引数が2つ」**です。

ややこしい日本語ですが、`Aggregate((x, y) => x + y)`の`x`と`y`の事です。

`x`はいままで計算してきた結果、`y`は各要素の値です。

分かりづらいので、置き換えてみます。

``` c#
while (enumerator.MoveNext())
{
    // ここの部分が指定したメソッドに置き換わる
	val = func(val, enumerator.Current);
}
return val;
```

↓

``` c#
// (x, y) => x + yと置き換えると、valがx,enumerator.Currentがyなので
while (enumerator.MoveNext())
{
	val = val + enumerator.Current
}
return val;
```

こうして合計を求める事ができました。

また、戻り値の型は**データリストの要素の型**です。(今回は`Select`で取り出した`double`)

### その2

次は2つ目。

``` c#
// 要素数を求める
var count = IDOLList.Aggregate(0, (x, y) => x + 1);
// 中身はこんな感じです
public static TAccumulate Aggregate<TSource, TAccumulate>(this IEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate, TSource, TAccumulate> func)
{
	if (source == null)
	{
		throw Error.ArgumentNull("source");
	}
	if (func == null)
	{
		throw Error.ArgumentNull("func");
	}
    // 初期値が指定した値になる
	TAccumulate val = seed;
	foreach (TSource item in source)
	{
        // 今回の場合、valがx,itemがyにあたる
		val = func(val, item);
	}
	return val;
}
```

1つ目ほぼ同じですが、初期値を指定することができます。

なので今回のケースで置き換えると、

``` c#
// ここで初期値を設定し
TAccumulate val = seed;
foreach (TSource item in source)
{
    // 指定したメソッドをぶん回す
	val = func(val, item);
}
return val;
```

↓

``` c#
// 初期値は0、メソッドは(x, y) => x + 1なので、
TAccumulate val = 0;
foreach (TSource item in source)
{
	val = val + 1
}
return val;
```

ここで少し注意事項ですが、`Aggregate`の第2引数のラムダ式は、**1つ目の引数は初期値と同じ型**で、**2つ目の引数はデータリストの要素と同じ型**です。

第2引数は`Func<TAccumulate, TSource, TAccumulate> func`です。`TAccumulate`は第1引数から推論されます。`TSource`はデータリストから推論されます。そして戻り値は`TAccumulate`になり、これはラムダ式の戻り値から推論されます。推論だらけですね。

### その3

最後に3つ目です。

``` c#
// 身長の平均を求める
var aveHeight = IDOLList.Select(x => x.Height)
                        .Aggregate(
                          (height: 0.0, count: 0), 
                          (x, y) => x = (x.height + y, x.count + 1), 
                          z => z.height / z.count);
// 中身はこんな感じです
public static TResult Aggregate<TSource, TAccumulate, TResult>(this IEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate, TSource, TAccumulate> func, Func<TAccumulate, TResult> resultSelector)
{
	if (source == null)
	{
		throw Error.ArgumentNull("source");
	}
	if (func == null)
	{
		throw Error.ArgumentNull("func");
	}
	if (resultSelector == null)
	{
		throw Error.ArgumentNull("resultSelector");
	}
    TAccumulate val = seed;
	foreach (TSource item in source)
	{
         // 今回の場合、valがx,itemがyにあたる
		val = func(val, item);
	}
   　// 今回の場合、これがzです
	return resultSelector(val);
}
```

2つ目とほぼ同じですが、戻り値に対して最終処理を行えます。

例によって置き換えてみます。

``` c#
// ここで初期値を設定し
TAccumulate val = seed;
foreach (TSource item in source)
{
    // 指定したメソッドをぶん回し
	val = func(val, item);
}
// 最終処理をして終わり
return resultSelector(val);
```

↓

``` c#
// 初期値：(height: 0.0, count: 0.0)
// 計算処理：(x, y) => x = (x.height + y, x.count + 1)
// 最終処理：z => z.height / z.count

TAccumulate val = (height: 0.0, count: 0.0);
foreach (TSource item in source)
{
	val = (val.height + y, x.count + 1);
}
return val.height / val.count;
```

ここでの注意事項として、最終的な戻り値は、`Aggregate`の**第3引数のラムダ式の戻り値です。**また第2引数のラムダ式に関しては2つ目と同じです。

C#でそれなりの量のコードを書いてきましたが、今の所一度も使った事はありません。

# 単体取得系

データリストの中から、1つだけ要素を取り出す事ができるメソッド達です。

また、~~大して変わらない~~大人の事情で、2つ同時に解説します。

## First, FirstOrDefault

```c#
// 1人目のアイドル
var idolFirst = IDOLList.First();
// 条件の指定も可能
var after20First = IDOLList.FirstOrDefault(x => x.Age > 19);
```

条件に一致した最初の要素を取り出します。特に条件を指定しない場合、1つ目の要素が取り出せます。

2つの違いですが、条件を満たす要素が見つからなかった時の挙動で、`First`は例外が発生し、`FirstOrDefault`は既定値を返します。

単体取得系の中では一番人気だと思います。

## Last, LastOrDefault

``` c#
// 最後のアイドル
var idolLast = IDOLList.Last();
// 条件の指定も可能
var under149Last = IDOLList.LastOrDefault(x => x.Height < 150);
```

条件に一致した最後の要素を取り出します。特に条件を指定しない場合、最後の要素が取り出せます。

2つの違いですが、条件を満たす要素が見つからなかった時の挙動で、`Last`は例外が発生し、`LastOrDefault`は既定値を返します。

たまに使います。

## Single, SingleOrDefault

```c#
// 単独のアイドル
var idolSingle = IDOLList.Single();
// 条件の指定も可能
var tubasaSingle = IDOLList.SingleOrDefault(x => x.Name == "伊吹 翼");
```

条件に一致した唯一の要素を取り出します。条件を満たす要素が複数あった場合、例外が発生します。特に条件を指定しない場合、ちょっと特殊な挙動をします。

- `Single`の場合
  - 要素数が1つならば、それを返す。0、もしくは2つ以上の場合例外。
- `SingleOrDefault`の場合
  - 要素数が1つならば、それを返す。0ならば既定値を返す。2つ以上の場合例外。

2つの違いですが、条件を満たす要素が見つからなかった時の挙動で、`Single`は例外が発生し、`SingleOrDefault`は既定値を返します。

`SingleOrDefault`も、条件を満たす要素が複数あった場合、例外が発生します。使った事は1度もないです。

## ElementAt, ElementAtOrDefault

``` c#
// インデックスで指定
var idolElement = IDOLList.ElementAt(5);
// インデックスで指定
var overRange = IDOLList.ElementAtOrDefault(50000000);
```

インデックスで指定した場所の要素を取得します。特に条件とは設定できません。

2つの違いですが、領域外(0未満や要素数以上の値)を設定した時の挙動で、`ElementAt`は例外が発生し、`ElementAtOrDefault`は既定値を返します。

`IEnumerable`のままインデックスで取得可能なので、便利な気がします。が、使った事はありません。

# 変換系

データリストをまるっと別の形に変えてしまうメソッド達です。

## AsEnumerable

``` c#
var idolEnumerable = IDOLList.AsEnumerable();
```

`IEnumerable`に変換します。外部クラス等から`List`や配列で受け取ったデータを`IEnumerable`で公開したい時に使います。今の所、そんな局面に出会った事はないですが。

主な使いみちは2つあるそうです。

1つ目は、隠蔽されてしまった拡張クラスのメソッドを呼び出すとき。

拡張メソッドは、インスタンスメソッドよりも呼び出しの優先順位が低いです。

なので、

```c#
private class MyList<T> : List<T>
{
    public bool Any()
    {
        return false;
    }
}
```

こんなクラスを作って、`Any`を実装してしまうと、LINQの`Any`が呼び出せなくなります。

``` C#
var myList = new MyList<IDOL>{ new IDOL{ Name = "俺" }};
var alwaysFalse = myList.Any(); // 常にfalse
```

こんな時にこいつの出番です。

``` c#
var myList = new MyList<IDOL>{ new IDOL{ Name = "俺" }};
var correctlyJudge   = myList.AsEnumerable().Any(); // LINQのAnyが呼ばれる
```

2つ目は、LINQ to SQL、つまりデータベースとデータをやり取りするときに使います。

詳しくは[こちら](https://csharptan.wordpress.com/2011/12/09/2%E7%A8%AE%E9%A1%9E%E3%81%AElinq/)をご覧ください。

ちなみに驚くほどシンプルな実装です。

``` c#
public static IEnumerable<TSource> AsEnumerable<TSource>(this IEnumerable<TSource> source)
{
    return source;
}
```

## ToList

``` c#
var bustList = IDOLList.Select(x => x.Bust).ToList();
var bust = bustList[5];
```

`List`に変換します。

注意事項ですが、**必要になるまで実行しないようにしてください。**

いろいろな人がいろいろな所で言ってるので理由は割愛。~~(面倒だし)~~

``` c#
// ToList()することで、結果が生成されるが
var bustList = IDOLList.Where(x => x.Age < 13).Select(x => x.Bust).ToList();
// 結局foreachでもう一度それらを確認することになる。
foreach(var b in bustList)
{
    Console.WriteLine(b);
}
```

わりとよく見るコードですが、LINQ警察の大好物です。

``` c#
// そのままにして
var bustList = IDOLList.Where(x => x.Age < 13).Select(x => x.Bust);
// foreachで結果を生成しましょう
foreach(var b in bustList)
{
    Console.WriteLine(b);
}
```

こうやってLINQ警察を追い返しましょう。

## ToArray

``` c#
var hipAry = IDOLList.Select(x => x.Hip).ToArray();
var hip = hipAry[5];
```

配列に変換します。注意事項は`ToList`と同じです。

## ToDictionary

``` c#
// 名前で検索して、元データ(IDOLクラス)を取り出す辞書
var idolDict = IDOLList.ToDictionary(x => x.Name);
// 名前で検索して、読み仮名を取り出す辞書
var phoneticDict = IDOLList.ToDictionary(x => x.Name,y => y.Phonetic);
// 検索キーが重複すると、例外発生
var exceptionDict = IDOLList.ToDictionary(x => x.Age);
```

`Dictionary`に変換します。取り出す形式は特に指定しなければ元のデータ、指定した場合はその型です。

注意事項ですが、**重複する値をキーに指定すると例外が発生します。**(アイマスに同姓同名のキャラはいないが、年齢が同じキャラはいる)

## ToLookup

``` c#
// 名前で検索して、元データ(IDOLクラス)を取り出すルックアップテーブル
var idolLook = IDOLList.ToLookup(x => x.Name);
// 名前で検索して、読み仮名を取り出すルックアップテーブル
var phoneticLook = IDOLList.ToLookup(x => x.Name,y => y.Phonetic);
// 検索キーが重複しても、問題なし
var noexceptionLook = IDOLList.ToLookup(x => x.Age);
// 14歳組
var age14 = noexceptionLook[14];
```

`ILookup`に変換します。`Dictionary`に似ていますが、`Dictionary`が**一致する単一の要素**を取り出すのに対して、`ILookup`は**一致する要素のコレクション**を取り出します。

## ToHashSet

``` c#
// 重複した値が削除される
var idolSet = IDOLList.ToHashSet();
// 比較方法を指定することも可能
var idolAgeSet = IDOLList.ToHashSet(new IdolCompare());

class IdolCompare : IEqualityComparer<IDOL>
{
    public bool Equals(IDOL x, IDOL y)
    {
        return x.Age == y.Age;
    }

    public int GetHashCode(IDOL obj)
    {
        return obj.Age.GetHashCode();
    }
}
```

`HashSet`に変換します。やっている事自体は`Distinct`と同じです。なので、注意事項も同じ。

何が違うかというと、`ToHashSet`は`HashSet`クラスに変換します。なので、ここで評価が実行されます(即時評価)。

一方`Distinct`は`IEnumerable`のままなので、評価は実行されません(遅延評価)。

使い分けの基準としては、

- まだLINQのメソッドを呼ぶ場合や、その後`foreach`を実行する場合、`Distinct`
- もうこれ以上LINQのメソッドを呼ばず、結果を利用する場合は、`ToHashSet`

でいいと思います。

# ソート系

ソートするメソッド達です。以上です。

## OrderBy, OrderByDescending

``` c#
// 昇順
var ascendingIdol = IDOLList.OrderBy(x => x.Bust);
// 降順
var descendingIdol = IDOLList.OrderByDescending(x => x.Bust);
// 外部のデータを使ってソートも出来る(ランダムに並び替え)
var rondomIdol = IDOLList.OrderBy(x=> Guid.NewGuid());

// ソート較方法を指定することも可能
var ascendingIdol2 = IDOLList.OrderBy(x => x, new IdolCompare());
var descendingIdol2 = IDOLList.OrderByDescending(x => x.Bust, new IdolCompare());

class IdolCompare :　IComparer<IDOL>
{
    public int Compare(IDOL x, IDOL y)
    {
        return x.Bust - y.Bust;
    }
}
```

指定されたキーでソートします。`IComparer`インターフェイスによってソート方法を指定することも可能です。

## ThenBy, ThenByDescending

```c#
// 昇順→降順
var ascendingIdol = IDOLList.OrderBy(x => x.Bust).ThenByDescending(x=>x.Waist);
// 降順→昇順
var descendingIdol = IDOLList.OrderByDescending(x => x.Bust).ThenBy(x=>x.Waist);
// ソート較方法を指定することも可能
var ascendingIdol2 = IDOLList.OrderBy(x => x, new IdolCompareB()).ThenByDescending(x => x, new IdolCompareW());
var descendingIdol2 = IDOLList.OrderByDescending(x => x, new IdolCompareB()).ThenBy(x => x, new IdolCompareW());

class IdolCompareB :　IComparer<IDOL>
{
    public int Compare(IDOL x, IDOL y)
    {
        return x.Bust - y.Bust;
    }
}

class IdolCompareW :　IComparer<IDOL>
{
    public int Compare(IDOL x, IDOL y)
    {
        return x.Waist - y.Waist;
    }
}
```

`OrderBy` と`OrderByDescending`を実行後のみ、呼び出せるメソッドです。`OrderBy`や`OrderByDescending`で同一だった場合の副条件を設定できます。当然`IComparer`インターフェイスによってソート方法を指定することも可能です。

# 範囲取得系

データリストから範囲を指定して取得するメソッド達です。

## Skip

``` c#
// 前から100人飛ばして、後ろの177人が残る
var hundredSkip = IDOLList.Skip(100);
```

引数で指定した数だけ飛ばして、残った要素を返します。この時、要素数よりも大きな数を指定しても例外は発生しません。

## SkipWhile

``` c#
// 最初に年齢が0歳のキャラが出るまで飛ばして、残りを返す。
var age0Skip = IDOLList.SkipWhile(x => x.Age != 0);
// インデックス付きもある
var indexedSkip = IDOLList.SkipWhile((x, i) => i < 100);
```

引数で指定した条件に一致する要素が出てくるまで飛ばして、残った要素を返します。この時、最後まで一致しなくても例外は発生しません。

## Take

```c#
// 前から100人取得して、後ろのは飛ばす
var hundredTake = IDOLList.Take(100);
```

引数で指定した数だけ取得して、残った要素を飛ばします。この時、要素数よりも大きな数を指定しても例外は発生しません。

## TakeWhile

```c#
// 最初に年齢が0歳のキャラが出るまで取得して、残りを飛ばす。
var age0Skip = IDOLList.SkipWhile(x => x.Age != 0);
// インデックス付きもある
var indexedSkip = IDOLList.SkipWhile((x, i) => i < 100);
```

引数で指定した条件に一致する要素が出てくるまで取得し、残った要素を飛ばします。この時、最後まで一致しなくても例外は発生しません。

見れば分かると思いますが、

- `Skip` ⇔ `Take`
- `SkipWhile` ⇔ `TakeWhile`

と対応する関係になってます。

# 結合系

複数のデータリストを様々な方法でくっつけるメソッド達です。

この辺から、結構説明が面倒くさいやつが増えてきます。

## Concat

``` c#
// 向井 拓海, 藤本 里奈, 松永 涼, 大和 亜季, 木村 夏樹
var enjin = IDOLList.Where(x => x.Unit.Contains("炎陣"));
// 向井 拓海, 藤本 里奈
var naughty = IDOLList.Where(x => x.Unit.Contains("ノーティギャルズ"));
// 向井 拓海, 藤本 里奈, 松永 涼, 大和 亜季, 木村 夏樹, 向井 拓海, 藤本 里奈
var gocha = enjin.Concat(naughty);
```

元のデータリストの後ろに、引数で指定したデータリストをくっつけます。

ただの結合なので、重複してても関係ありません。

## Zip

``` c#
// 星井 美希, 高山 紗代子, 天空橋 朋花, 永吉 昴, 二階堂 千鶴
var milky = IDOLList.Where(x => x.Unit.Contains("ミルキーウェイ"));
// 中谷 育, 七尾 百合子, 松田 亜利沙
var birth = IDOLList.Where(x => x.Unit.Contains("トゥインクルリズム"));
// (星井 美希, 中谷 育), (高山 紗代子, 七尾 百合子), (天空橋 朋花, 松田 亜利沙)
var gocha = milky.Zip(birth, (x, y) => (x.Name, y.Name));
```

2つのデータリストから1つのデータリストを作ります。

第1引数で指定したデータリストと元のデータリストを、第2引数のラムダ式で変換して1つのデータリストにまとめます。

注意事項ですが、最終的な要素数は**2つのデータリストのうち少ない方と同じ**です。今回の場合は、上のリストの方が長いので、後ろ2人は無視されます。

## Union

``` c#
// 森久保 乃々, 星 輝子, 早坂 美玲
var indivi = IDOLList.Where(x => x.Unit.Contains("インディヴィジュアルズ"));
// 輿水 幸子, 星 輝子, 白坂 小梅 
var kawaii = IDOLList.Where(x => x.Unit.Contains("カワイイボクと142's"));
// 森久保 乃々, 星 輝子, 早坂 美玲, 輿水 幸子, 白坂 小梅
var syokoHarem = indivi.Concat(under);
```

元のデータリストと、引数で指定したデータリストの和集合を求めます。

和集合なので重複は削除されます。

## Intersect

``` c#
// 速水 奏, 塩見 周子, 宮本 フレデリカ, 一ノ瀬 志希, 城ヶ崎 美嘉
var lipps = IDOLList.Where(x => x.Unit.Contains("LiPPS"));
// 速水 奏, 小松 伊吹
var luminous = IDOLList.Where(x => x.Unit.Contains("ルミナスボーダー"));
// 速水 奏
var momiyade = lipps.Intersect(luminous);
```

元のデータリストと、引数で指定したデータリストの積集合を求めます。

積集合なので、重複したもののみが残ります。

## Except

```c#
// 渋谷 凛, 本田 未央, 島村 卯月
var newGene = IDOLList.Where(x => x.Unit.Contains("ニュージェネレーション"));
// 渋谷 凛、神谷 奈緒、北条 加蓮
var luminous = IDOLList.Where(x => x.Unit.Contains("トライアドプリムス"));
// 本田 未央, 島村 卯月
var udumio = newGene.Except(luminous);
```

元のデータリストから、引数で指定したデータリストを引いた差集合を求めます。

「元のデータリストにない要素」が、「引数のデータリストにある」状態でも例外は発生しません。

## GroupBy

```c#
// 名前で検索して、元データ(IDOLクラス)を取り出すグループ
var idolGroup = IDOLList.GroupBy(x => x.Name);
// 名前で検索して、読み仮名を取り出すグループ
var phoneticGroup = IDOLList.GroupBy(x => x.Name,y => y.Phonetic);
// 検索キーが重複しても、問題なし
var noexceptionGroup = IDOLList.GroupBy(x => x.Age);
// 14歳組
var age14 = noexceptionGroup.First(x => x.Key == 14);
```

結合系では無いような気もしますが、他に場所が無かったのでここで説明します。

変換系で説明した`ToLookup`とほぼ同じです。違いは評価のタイミングです。

`ToLookup`は`ILookup`インターフェイスに変換します。なので、ここで評価が実行されます(即時評価)。

一方`GroupBy`は`IEnumerable`のままなので、評価は実行されません(遅延評価)。

`ToHashSet`に対する`Distinct`と同じです。なので、使い分けもそちらと同様です。

## Join

``` c#
// 作品識別クラス
class Work
{
    public int ID { get; set; }
    public string Name { get; set; }
}
// 作品一覧
var workList = new List<Work>{
  new Work{ ID = 1, Name = "AllStarts" },
  new Work{ ID = 2, Name = "DearlyStars" },
  new Work{ ID = 3, Name = "CinderellaGirls" },
  new Work{ ID = 4, Name = "MillionLive" },
  new Work{ ID = 5, Name = "ShinyColors" },
};
// 名前と作品名のタプル
var workName = workList.Join(
    IDOLList,
    x => x.ID,
    y => y.WorkID,
    (x, y) => (Work: x.Name, y.Name));
```

元のデータリストに、引数で指定したデータリストを結合します。

引数を順番に説明すると、

1. 結合したいデータリスト
   - 2,3で指定するキーを使って、元のデータと結びつけるデータです。
2. 元のデータのキー
   - 3のキーと比較されます。
3. 結合したいデータのキー
   - 2のキーと比較されます。
4. 最終的に出力する結果生成用のラムダ式
   - このラムダ式の戻り値の`IEnumerable`が生成されます。

こんな感じです。

## GroupJoin

``` c#
// 作品識別クラス
class Work
{
    public int ID { get; set; }
    public string Name { get; set; }
}
// 作品一覧
var workList = new List<Work>{
  new Work{ ID = 1, Name = "AllStarts" },
  new Work{ ID = 2, Name = "DearlyStars" },
  new Work{ ID = 3, Name = "CinderellaGirls" },
  new Work{ ID = 4, Name = "MillionLive" },
  new Work{ ID = 5, Name = "ShinyColors" },
};
// 作品をキーとして、「作品と名前のタプル」の列挙を作成
var workNameDouji = workList.GroupJoin(
    IDOLList,
    x => x.ID,
    y => y.WorkID,
    (x, y) => (x, y.Select(x => x.Name)));
```

結合した後に、指定したキーでグループ化します。

引数を順番に説明すると、

1. 結合したいデータリスト
   - 2,3で指定するキーを使って、元のデータと結びつけるデータです。
2. 元のデータのキー
   - 3のキーと比較されます。
3. 結合したいデータのキー
   - 2のキーと比較されます。
4. 最終的に出力する結果生成用のラムダ式
   - このラムダ式の戻り値が、各グループに配置される`IEnumerable`になります。

こんな感じです。`Join`とほとんど同じです。

# その他

どこに入れたら良いかわからないもの達です。

## Reverse

``` c#
// 昇順
var ascendingIdol = IDOLList.OrderBy(x => x.Bust);
// 降順を反転→昇順
var descendingReverseIdol = IDOLList.OrderByDescending(x => x.Bust).Reverse();
```

データリストをひっくり返します。

ただひっくり返すだけなので、上記の2つが同一のデータリストを返すとは限りません。

``` c#
// trueになるとは限らない(同一の値が存在する場合)
var rslt = ascendingIdol.SequenceEqual(descendingReverseIdol);
```

- 元データ(一部抜粋)

| Name      | Bust |
| --------- | ---- |
| 如月 千早 | 72   |
| 及川 雫   | 105  |
| 中谷 育   | 72   |
| 横山 千佳 | 65   |

- `OrderBy(x => x.Bust)`

| Name      | Bust |
| --------- | ---- |
| 横山 千佳 | 65   |
| 如月 千早 | 72   |
| 中谷 育   | 72   |
| 及川 雫   | 105  |

- `OrderByDescending(x => x.Bust)`

| Name      | Bust |
| --------- | ---- |
| 及川 雫   | 105  |
| 如月 千早 | 72   |
| 中谷 育   | 72   |
| 横山 千佳 | 65   |

- `OrderByDescending(x => x.Bust).Reverse()`

| Name      | Bust |
| --------- | ---- |
| 横山 千佳 | 65   |
| 中谷 育   | 72   |
| 如月 千早 | 72   |
| 及川 雫   | 105  |

## Append, Prepend

``` c#
// 星井 美希, 我那覇 響, 四条 貴音
var fairy = IDOLList.Where(x => x.Unit.Contains("プロジェクト・フェアリー"));
// 俺、アイドルになります。
var newIDOL = new IDOLViewData { Name = "俺" };
// 星井 美希, 我那覇 響, 四条 貴音, 俺
var fairyore = fairy.Append(newIDOL);
// 俺, 星井 美希, 我那覇 響, 四条 貴音
var orefairy = fairy.Prepend(newIDOL);
```

先頭や末尾に新たな要素を追加します。

注意事項ですが、`Add`と異なり、**元の要素は変更されません。**あくまで新しい`IEnumerable`を返すメソッドです。

## DefaultIfEmpty

``` c#
// さすがにそんなに大きな人はいない
var bigBust = IDOLList.Where(x => x.Bust > 110);
// そもそもforeachの中に入らない
foreach(var bb in bigBust)
{
    // 呼ばれることなく終わる
    Console.WriteLine(bb.Name);
}
// 規定の値を返す
foreach(var bb in bigBust.DefaultIfEmpty())
{
   try
   {
       // 例外が発生(クラスの既定はnull)
       Console.WriteLine(bb.Name);
   }
   catch(Exception e)
   {
       Console.WriteLine(e.Message);
   }
}
// 規定の値を指定することも可能
foreach(var bb in bigBust.DefaultIfEmpty(new IDOL { Name = "俺" }))
{
    // 俺
    Console.WriteLine(bb.Name);
}
```

要素数が0の時、代わりに返す値を設定できるメソッドです。

検索結果が見つからなかった時に返すメッセージを設定しておくと、捗るかもしれませんね。

``` c#
var over40 = IDOLList.Where(x => x.Age > 40).Select(x => x.Name).DefaultIfEmpty("404");
foreach(var o in over40)
{
    Console.WriteLine(o);
}
```

## Range, Repeat, Empty

``` c#
// 0,1,2,3,4,5,6,7,8,9
var range = Enumerable.Range(0, 10);
// 0,0,0,0,0,0,0,0,0
var repeat = Enumerable.Repeat(0, 10);
// 素寒貧
var empty = Enumerable.Empty<int>();

// foreachでforっぽいことが出来る
foreach(var i in range)
{
    // 0123456789
    Console.Write(i);
}
// 初期値を指定した配列が出来る
var zeroAry = repeat.ToArray();
```

新たな`IEnumerable`を作り出してくれるメソッド達です。

`Range`は初期値と回数を指定します。初期値から1ずつインクリメントされた`IEnumerable`が返ってきます。

`Repeat`は設定値と回数を指定します。`Range`とは異なり、設定値を回数分繰り返した`IEnumerable`が返ってきます。

`Empty`は空っぽの`IEnumerable`が返ってきます。引数がなにもないので、ジェネリックで型を指定します。有効活用できる場面が思いつかなかったので、誰か教えてください。

# あとがき

一気呵成に書き上げました。~~左手が攣りそうです。~~

`Join`と`GroupBy`と`GroupJoin`はそのうち個別に何か書こうかと思ってます。(~~思ってるだけ~~)

「その説明間なんかオカシイで」とか「このメソッドもっと詳しく」とか「何いってんだか分かんねーぞ」とかあったらコメントとか編集リクエストとかでお願いします。