C#における、LINQと配列操作

LINQとは、コレクションの操作をするために用意された機能のことである。

ちなみに、LINQとは「Language Integrated Query」の略。

## LINQを使う
LINQを使うには、名前空間で

~~~csharp
using System.Linq;
~~~
のようにusingディレクティブで指定する。

なお、以降使用するリストを

~~~csharp
var arrayData1 = new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
var arrayData2 = new List<int>() { 2, 3, 1, 4, 9, 6, 5, 10, 7, 8 };
var arrayData3 = = new List<Pokemon>() {
    new Pokemon("リザードン", new string[]{ "ほのお", "ひこう" }, true }),
    new Pokemon("ピカチュウ", new string[]{ "でんき" }, true },
    new Pokemon("メタグロス", new string[]{ "はがね", "エスパー" }, false },
    new Pokemon("ゲッコウガ", new string[]{ "みず", "あく" }, true },
    new Pokemon("ドラパルト", new string[]{ "ドラゴン", "ゴースト" }, false },
    new Pokemon( "エルレイド", new string[]{ "エスパー", "かくとう" }, false },
    new Pokemon("ミュウツー", new string[]{ "エスパー" }, true },
    new Pokemon("パルキア", new string[]{ "みず", "ドラゴン" }, true },
    new Pokemon("マリルリ", new string[]{ "みず", "フェアリー" }, true },
    new Pokemon("ゲンガー", new string[]{ "ゴースト", "どく" }, false },
    new Pokemon("ボルケニオン", new string[]{ "ほのお", "みず" }, true },
};
~~~
とする。

また、

~~~csharp:Pokemonクラス
class Pokemon
    {
        public string Name { get; set; }
        public string[] Types { get; set; }
        public bool IsGet { get; set; }

        public Pokemon(string name, string[] types, bool isGet)
        {
            Name = name;
            Types = types;
            IsGet = isGet;
        }
    }
~~~
とする。

## LINQで使えるメソッド
LINQでできるコレクションを操作するメソッドは以下の通り

### Where
Whereメソッドは、条件に一致した要素を取得する。

~~~csharp:(例1)arrayData1から5以上の数字だけを表示したい時
var arrayDataByWhere = arrayData1.Where(el => el > 4); 

foreach(var item in arrayDataByWhere) {
  Console.WriteLine(item);
}
~~~

~~~csharp:出力結果
5
6
7
8
9
10
~~~

~~~csharp:(例2)arrayData3の各要素のNameが4文字のデータを表示したい時
var PokemonData = arrayData3.Where(el => el.Name.Length == 4); 

foreach(var item in PokemonData) {
  Console.WriteLine(item.Name);
}
~~~

~~~csharp:出力結果
パルキア
マリルリ
ゲンガー
~~~


### Select
Selectメソッドは、コレクションの各要素を別の値に変換する。

~~~csharp:(例)arrayDataの各要素に2をかけた配列を新たに作りたい時
var arrayDataBySelect = arrayData1.Select(el => el * 2); 

foreach(var item in arrayDataBySelect) {
  Console.WriteLine(item);
}
~~~

~~~csharp:出力結果
2
4
6
8
10
12
14
16
18
20
~~~

## OrderBy
OrderByメソッドは、昇順に並び替えるメソッド。

~~~csharp:(例)arrayData2を照準に並び替えたい時
var arrayDataBySelect = arrayData2.OrderBy(el => el); 

foreach(var item in arrayDataBySelect) {
  Console.WriteLine(item);
}
~~~

~~~csharp:出力結果
1
2
3
4
5
6
7
8
9
10
~~~

## OrderByDecending
OrderByメソッドは、降順に並び替えるメソッド。

~~~csharp:(例1)arrayData2を降順に並び替えたい時
var arrayDataBySelect = arrayData2.OrderBy(el => el); 

foreach(var item in arrayDataBySelect) {
  Console.WriteLine(item);
}
~~~

~~~csharp:出力結果
10
9
8
7
6
5
4
3
2
1
~~~

~~~csharp:(例2)arrayData3の要素の名前がカナ順になるように並び替えたい時
var arrayDataBySelect = arrayData3.OrderBy(el => el.Name); 

foreach(var item in arrayDataBySelect) {
  Console.WriteLine(item.Name);
}
~~~

~~~csharp:出力結果
エルレイド
ゲッコウガ
ゲンガー
ドラパルト
パルキア
ピカチュウ
ボルケニオン
マリルリ
ミュウツー
メタグロス
リザードン
~~~

## Enumerable
### Repeat
要素が1というint型の値を何個も持つコレクションもしくはList<T>を作りたい場合などに使用する。

~~~csharp:(例1)10個の1をList<T>を作成する時
var data = Enumerable.Repeat(1, 10).ToList();
foreach (int i in data)
{
     Console.WriteLine(i);
}
~~~

~~~csharp:出力結果
1
1
1
1
1
1
1
1
1
1
~~~

~~~csharp:(例2)5個の1を配列で作成する時
var data = Enumerable.Repeat(1, 5).ToArray();
foreach (int i in data)
{
     Console.WriteLine(i);
}
~~~

~~~csharp:出力結果
1
1
1
1
1
~~~

空の配列を定義して、forやforeachのループでやるより遥かに楽である。

### Range
コレクションに1,2,3...nと連続した値をセットしたい場合に使用する。

~~~csharp:(例)
var data11 = Enumerable.Range(1, 20).ToArray();
foreach (int i in data11)
{
    Console.WriteLine(i);
}
~~~

## Average
コレクションの平均値を求める。

ちなみに、
・要素がint型の数値だけがまとまったものの平均値を求める(A)
・オブジェクトの要素の数値だけを参照して平均値を求める(B)
で使い方が異なるので注意。

~~~csharp:(A)の求め方
var list = new List<int> { 1,2,3,4,5 };
Console.WriteLine(list.Average());
~~~

(B)の求め方の場合、

~~~csharp:Personクラス
public class Person {
      public string FirstName { get; set; }
      public string LastName { get; set; }
      public int Age { get; set; }

      public Person(string fn, string ln, int age)
      {
           FirstName = fn;
           LastName = ln;
           Age = age;
      }
}
~~~
があったとする。

~~~csharp:(B)の求め方
List<Person> list2 = new List<Person>() {
      new Person("ジョルノ", "ジョバーナ", 15),
      new Person("ナルト", "うずまき", 17),
      new Person("一護", "黒崎", 17),
      new Person("義勇", "富岡", 21),
      new Person("月", "夜神", 20),
      new Person("リョーマ", "越前", 12),
      new Person("一馬", "桐生", 53),
      new Person("クラウド", "ストライフ", 24),
      new Person("葉", "麻倉", 13),
      new Person("威", "浅倉", 25),
};

var ave = list2.Average(el => el.Age);
Console.WriteLine(ave);
~~~

~~~csharp:出力結果
21.7
~~~

この場合はラムダ式を使う。


## Sum
コレクションの合計を求める。
Averageと同じような記法で使用する

~~~csharp:(A)の求め方
list.Sum();
~~~

~~~csharp:(B)の求め方
list2.Sum(el => el.Age);
~~~

## Min・Max
コレクションの最大・最小値を求める。
Averageと同じような記法で使用する

~~~csharp:(A)の求め方
list.Max();
~~~

~~~csharp:(B)の求め方
list2.Min(el => el.Age);
~~~

## Contains
変数に指定した文字があるかどうかを判定する

~~~csharp
string name = "竈門炭治郎";
bool check1 = name.Contains("竈門"); //　True
bool check2 = name.Contains("水柱"); //　False
~~~

## Any
変数の中に条件に一致しているデータがあるかどうかをチェックする。

~~~csharp
bool check = arrayData3.Any(el => el.IsGet); // True
~~~

## All
変数の中のデータが全て条件を満たすかどうかをチェックする

~~~csharp
bool check = arrayData3.All(el => el.IsGet); // False
~~~

## SequenceEqual
2つのコレクションの要素が完全一致しているかどうかをチェックする。

例えば、

~~~csharp
var pokemons = new List<Pokemon>() {
    new Pokemon("カメックス", new string[]{ "みず" }, true),
    new Pokemon("ミミッキュ", new string[]{ "ゴースト", "フェアリー" }, false),
    new Pokemon("ガブリアス", new string[]{ "ドラゴン", "じめん" }, true),
    new Pokemon("ヤドキング", new string[]{ "みず", "エスパー" }, false),
    new Pokemon("キングドラ", new string[]{ "みず", "ドラゴン" }, true),
    new Pokemon("マフォクシー", new string[]{ "ほのお", "エスパー" }, true),
    new Pokemon("ドリュウズ", new string[]{ "じめん", "はがね" }, false),
    new Pokemon("エレキブル", new string[]{ "でんき" }, true),
    new Pokemon("ブーバーン", new string[]{ "ほのお" }, true),
    new Pokemon("ベトベトン", new string[]{ "どく" }, false),
    new Pokemon("ハッサム", new string[]{ "むし", "はがね" }, true),
};
~~~
という値とarrayData3を比較するなどの場合に使える

~~~csharp
arrayData3.SequenceEqual(pokemons); //False
~~~

## Distinct
コレクションから重複を削除する

~~~csharp:(例)
var array = new List<int>() {1,1,2,2,3,3,4,4,5,5,6,6,7,7};
array.Distinct(); 
~~~

## Concat
2つのコレクションを連結する。

~~~csharp:(例)
List<Pokemon> sumPokemon = pokemons.Concat(pokemons2).ToList();
~~~

## ラムダ式
LINQで用意されているメソッドでは、「el => el > 4」というような、
「(変数) => (条件)」
の形式で書かれた部分のことをラムダ式といい、ラムダ式における「=>」をラムダ演算子という。

(変数)の名前は自由に決めていい。
elやres,req,valなど。

## 参考文献

https://gihyo.jp/book/2017/978-4-7741-8758-7

https://gihyo.jp/book/2019/978-4-297-10458-0