【C#】GroupBy()の使い方

# なりゆき

個人的にこの`GroupBy()`を何回か使ったことがあるのですが、戻り値の型がわかりづらく理解できていないためこの記事にまとめることにしました。理解する時に作成した図も貼り付けておきます。分かりづらかったらすみません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2519245/2ca4538c-ad2a-e946-ad18-94eb85aa6d24.png)

# 基本の使い方

下記のソース例ではグループ化するプロパティに`CountryName`を選択しています。

```C#:GroupByを使ってグループ化するソース例
List<City> cityList = new List<City>();

cityList.Add(new City { CountryName = "日本", CityName = "東京" });
cityList.Add(new City { CountryName = "日本", CityName = "大阪" });
cityList.Add(new City { CountryName = "アメリカ", CityName = "ロサンゼルス" });
cityList.Add(new City { CountryName = "アメリカ", CityName = "シアトル" });

//国名をグループ化するプロパティとして選択してグループ化する。
var groupByCountryName = cityList.GroupBy(x => x.CountryName);

class City
{
    public string CountryName { get; set; } = "";
    public string CityName { get; set; } = "";
}
```

グループ化した値の取得は下記のようにします。

```C#:グループ化した値を取得するソース例
List<City> cityList = new List<City>();

cityList.Add(new City { CountryName = "日本", CityName = "東京" });
cityList.Add(new City { CountryName = "日本", CityName = "大阪" });
cityList.Add(new City { CountryName = "アメリカ", CityName = "ロサンゼルス" });
cityList.Add(new City { CountryName = "アメリカ", CityName = "シアトル" });

//国名をグループ化するプロパティとして選択してグループ化する。
var groupByCountryName = cityList.GroupBy(x => x.CountryName);

foreach (var groupingCity in groupByCountryName)
{
    //Keyの中身はGroupByでグループ化したプロパティです。
    //この例では[CountryName]プロパティが値として取得できます。
    Console.WriteLine($"Key：{groupingCity.Key}");

    foreach (var city in groupingCity)
    {
        //グループ化した結果を列挙しています。
        Console.WriteLine($"国名：{city.CountryName}、都市名：{city.CityName}");
    }

    //出力
    //Key：日本
    //国名：日本、都市名：東京
    //国名：日本、都市名：大阪
    //Key：アメリカ
    //国名：アメリカ、都市名：ロサンゼルス
    //国名：アメリカ、都市名：シアトル
}

class City
{
    public string CountryName { get; set; } = "";
    public string CityName { get; set; } = "";
}
```

# 応用

複数の値でグループ化する場合は匿名型を使います。

```C#:複数の値でグループ化するソース例
List<City> cityList = new List<City>();

cityList.Add(new City { CountryName = "日本", CityName = "東京" ,AreaName = "新宿区" });
cityList.Add(new City { CountryName = "日本", CityName = "東京", AreaName = "品川区" });
cityList.Add(new City { CountryName = "日本", CityName = "大阪" , AreaName = "中央区" });
cityList.Add(new City { CountryName = "日本", CityName = "大阪", AreaName = "北区" });

//国名と都市名をグループ化するプロパティとして選択してグループ化する。
var groupByCountryName = cityList.GroupBy(x => new { x.CountryName ,x.CityName});

foreach (var groupingCity in groupByCountryName)
{
    //Keyの中身はGroupByでグループ化したプロパティです。
    //この例では[CountryName]プロパティと[CityName]プロパティが値として取得できます。
    Console.WriteLine($"Key：{groupingCity.Key.CountryName}、{groupingCity.Key.CityName}");

    foreach (var city in groupingCity)
    {
        //グループ化した結果を列挙しています。
        Console.WriteLine($"国名：{city.CountryName}、都市名：{city.CityName}、区名：{city.AreaName}");
    }

    //出力
    //Key：日本、東京
    //国名：日本、都市名：東京、区名：新宿区
    //国名：日本、都市名：東京、区名：品川区
    //Key：日本、大阪
    //国名：日本、都市名：大阪、区名：中央区
    //国名：日本、都市名：大阪、区名：北区
}

class City
{
    public string CountryName { get; set; } = "";
    public string CityName { get; set; } = "";
    public string AreaName { get; set; } = "";
}
```

KeyのValue部分のリストを取得したい場合はSingle()を使います。

```C#:KeyのValue部分のリストを取得するソース例
List<City> cityList = new List<City>();

cityList.Add(new City { CountryName = "日本", CityName = "東京" ,AreaName = "新宿区" });
cityList.Add(new City { CountryName = "日本", CityName = "東京", AreaName = "品川区" });
cityList.Add(new City { CountryName = "日本", CityName = "大阪" , AreaName = "中央区" });
cityList.Add(new City { CountryName = "日本", CityName = "大阪", AreaName = "北区" });

//国名と都市名をグループ化するプロパティとして選択してグループ化する。
var groupByCountryName = cityList.GroupBy(x => new { x.CountryName ,x.CityName}).ToList();

foreach (var groupingCity in groupByCountryName)
{
    //Keyの中身はGroupByでグループ化したプロパティです。
    //この例では[CountryName]プロパティと[CityName]プロパティが値として取得できます。
    Console.WriteLine($"Key：{groupingCity.Key.CountryName}、{groupingCity.Key.CityName}");

    //Keyは必ず唯一の要素になるのでSingle()を使ってValueの部分を取得しています。
    var groupingCityList = groupByCountryName.Single(x => x.Key == groupingCity.Key).ToList();

    foreach (var city in groupingCityList)
    {
        //グループ化した結果を列挙しています。
        Console.WriteLine($"国名：{city.CountryName}、都市名：{city.CityName}、区名：{city.AreaName}");
    }

    //出力
    //Key：日本、東京
    //国名：日本、都市名：東京、区名：新宿区
    //国名：日本、都市名：東京、区名：品川区
    //Key：日本、大阪
    //国名：日本、都市名：大阪、区名：中央区
    //国名：日本、都市名：大阪、区名：北区
}

class City
{
    public string CountryName { get; set; } = "";
    public string CityName { get; set; } = "";
    public string AreaName { get; set; } = "";
}
```

::: note warn 
注意事項
GroupBy()を遅延実行で使用する場合はSingle()を使うと例外が発生します。Single()を使って値を取得する場合はToList()等を使って値を確定させて下さい。上記のソースのGroupBy()を遅延実行になるように書き換えると例外が発生します。
:::

```C#:遅延実行に変更すると例外が発生します。
//国名と都市名をグループ化するプロパティとして選択してグループ化する。               ↓.Tolist()を消して遅延実行にした。
var groupByCountryName = cityList.GroupBy(x => new { x.CountryName ,x.CityName});
```