【C#】 LINQ のメソッド簡易解説 & 使い方まとめ【Unity】

Linqメソッド簡易解説&使い方まとめ

はじめに

このページでは、C# を扱う上で便利な LINQ の機能を紹介します。

LINQ とは?

LINQとは、配列やリストなどのコレクション要素を処理するメソッドを集めたライブラリのことです。
LINQを駆使することでコレクションの複雑な処理を簡潔にできたり、読みやすいコードが書けます。
使用するには、using ステートメントにて以下を宣言する必要があります。

using System.Linq;

便利な関数の紹介

要素の取得(単一)

ElementAt

指定したインデックス位置にある要素を返します。
ElementAtOrDefault だと、インデックスが範囲外の場合は既定値を返します。

int[] source = new[] { 5, 10, 22, 7, 3, 16 };
var result = source.ElementAt(2); 
// -> 22

First

シーケンスの最初の要素を返します。
指定した条件があれば、条件を満たす最初の要素を返します。
FirstOrDefault だと、要素が見つからない場合に既定値を返します。

int[] source = new[] { 5, 10, 22, 7, 3, 16 };
var result = source.First(); 
// -> 5

var result2 = source.First(e => e > 8); 
// -> 10

var result3 = source.FirstOrDefault(e => e > 30); 
// -> 0

Last

シーケンスの最後の要素を返します。
指定した条件があれば、条件を満たす最後の要素を返します。
LastOrDefault だと、シーケンスが空のときに既定値を返します。

int[] source1 = new[] { 5, 10, 22, 7, 3, 16 };
var result = source1.Last(); 
// -> 16

var result2 = source1.Last(e => e < 8); 
// -> 3

int[] source2 = new int[] { };
var result3 = source2.LastOrDefault(); 
// -> 0

Single

シーケンスの 1 つの特定の要素を返します。
要素が複数存在する場合は例外をスローします。
SingleOrDefault だと、シーケンスが空のときに既定値を返します。要素が複数の場合は例外が発生するので注意して下さい。

int[] source1 = new[] { 5, 10, 22, 7, 3, 16 };
var result = source1.Single(e => e > 20); 
// -> 22

var result2 = source1.Single(e => e > 8); 
// -> InvalidOperationException エラー

int[] source2 = new int[] { };
var result3 = source2.SingleOrDefault(); 
// -> 0

int[] source3 = new int[] { 1, 2, 3};
var result4 = source3.SingleOrDefault(); 
// -> InvalidOperationException エラー

要素の取得(複数)

Where

条件を満たす要素をすべて返します。

int[] source = new[] { 5, 10, 22, 7, 3, 16 };
var result = source.Where(e => e > 15); 
// -> 22,16

Distinct

重複を除いたシーケンスを返します。

int[] source = new[] { 5, 10, 22, 10, 3, 3 };
var result = source.Distinct(); 
// -> 5,10,22,3

Skip

先頭から指定した数の要素をスキップし、残りを返します。

int[] source = new[] { 5, 10, 22, 10, 3, 3 };
var result =  source.Skip(2); 
// -> 22,10,3,3

SkipWhile

指定した条件が満たされる限り先頭から要素をスキップした後、残りの要素を返します。

int[] source = new[] { 5, 10, 22, 30, 42, 50 };
var result = source.SkipWhile(e => e < 20); 
// -> 22,30,42,50

Take

先頭から指定した数の要素を返します。

int[] source = new[] { 5, 10, 22, 30, 42, 50 };
var result =  source.Take(3); 
// -> 5,10,22

TakeWhile

先頭から指定した条件を満たされる限りの要素を返した後、残りの要素をスキップします。

int[] source = new[] { 5, 10, 22, 30, 42, 50 };
var result =  source.TakeWhile(e => e < 15); 
// -> 5,10

集計

Max

最大値を返します。

int[] source = new[] { 5, 10, 22, 7, 3, 14 };
var result = source.Max(); 
// -> 22

Min

最小値を返します。

int[] source = new[] { 5, 10, 22, 7, 3, 14 };
var result = source.Min(); 
// -> 3

Sum

合計値を返します。

int[] source = new[] { 5, 10, 3 };
var result = source.Sum(); 
// -> 18

Average

平均値を返します。

int[] source = new[] { 5, 10, 3 };
var result = source.Average(); 
// -> 6

Aggregate

アキュムレータ関数を適用した結果を返します。
第一引数に初期値としてシード値を設定することもできます。

int[] source = new[] { 1, 2, 3 };

var result = source.Aggregate((n, next) => n + next * 2); 
// -> 11  (計算内容: 1 + (2*2) + (3*2) = 11)

// 初期値となるシード値 100 を設定
var result2 = source.Aggregate(100, (n, next) => n + next * 2); 
// -> 112  (計算内容: 100 + (1*2)+(2*2)+(3*2) = 112)

判定

All

すべての要素が条件を満たしているかを判断します。

int[] source = new[] { 1, 5, 2, 6 };
var result = source.All(e => e < 10); 
// -> True

var result2 = source.All(e => e > 5); 
// -> False

Any

条件を満たす要素があるかを判断します。

int[] source = new[] { 1, 5, 2, 6 };
var result = source.Any(e => e > 5); 
// -> True

Contains

指定した要素があるかどうかを調べます。

int[] source = new[] { 1, 5, 2, 6 };
var result = source.Contains(5); 
// -> True

var result = source.Contains(3); 
// -> False

SequenceEqual

2 つのシーケンスが等しいかどうかを判断します。

int[] source1 = new[] { 1, 5, 2, 6 };
int[] source2 = new[] { 1, 5, 7, 10 };
var result = source1.SequenceEqual(source2); 
// -> False

集合

Union

2つのシーケンスの和集合を生成します。

int[] source1 = new[] { 1, 5, 2, 6 };
int[] source2 = new[] { 1, 5, 7, 10 };
var result = source1.Union(source2);
// -> 1,5,2,6,7,10

Except

2つのシーケンスの差集合を生成します。

int[] source1 = new[] { 1, 5, 2, 6 };
int[] source2 = new[] { 1, 5, 7, 10 };
var result = source1.Except(source2);
// -> 2,6

Intersect

2つのシーケンスの積集合を生成します。

int[] source1 = new[] { 1, 5, 2, 6 };
int[] source2 = new[] { 1, 5, 7, 10 };
var result = source1.Intersect(source2);
// -> 1,5

ソート

OrderBy

要素を昇順に並べ替えます。

Pet[] pets = {
	new Pet { Name = "Pochi", Age = 4 },
	new Pet { Name = "Tama", Age = 8 },
	new Pet { Name = "Nyanko", Age = 1 },
	new Pet { Name = "Wanko", Age = 1 }
};

var result = pets.OrderBy(e => e.Age);
// -> {{ Name = Nyanko, Age = 1 },
//     { Name = Wanko, Age = 1 }
//     { Name = Pochi, Age = 4 }, 
//     { Name = Tama, Age = 8 }},

OrderByDescending

要素を降順に並べ替えます。

Pet[] pets = {
	new Pet { Name = "Pochi", Age = 4 },
	new Pet { Name = "Tama", Age = 8 },
	new Pet { Name = "Nyanko", Age = 1 },
	new Pet { Name = "Wanko", Age = 1 }
};

var result = pets.OrderByDescending(e => e.Age);
// -> {{ Name = Tama, Age = 8 },
//     { Name = Pochi, Age = 4 }, 
//     { Name = Nyanko, Age = 1 }
//     { Name = Wanko, Age = 1 }},

ThenBy

要素を並び替えた後、追加の条件で昇順に並べ替えます。

Pet[] pets = {
	new Pet { Name = "Pochi", Age = 4 },
	new Pet { Name = "Tama", Age = 8 },
	new Pet { Name = "Nyanko", Age = 1 },
	new Pet { Name = "Wanko", Age = 1 }
};

var result = pets.OrderBy(e => e.Age)
					 .ThenBy(e => e.Name.Length);
// -> {{ Name = Wanko, Age = 1 },
//     { Name = Nyanko, Age = 1 }
//     { Name = Pochi, Age = 4 }, 
//     { Name = Tama, Age = 8 }},

ThenByDescending

要素を並び替えた後、追加の条件で降順に並べ替えます。

Pet[] pets = {
	new Pet { Name = "Pochi", Age = 4 },
	new Pet { Name = "Tama", Age = 8 },
	new Pet { Name = "Nyanko", Age = 1 },
	new Pet { Name = "Wanko", Age = 1 }
};

var result = pets.OrderBy(e => e.Age)
					 .ThenByDescending(e => e.Name.Length);
// -> {{ Name = Nyanko, Age = 1 },
//     { Name = Wanko, Age = 1 }
//     { Name = Pochi, Age = 4 }, 
//     { Name = Tama, Age = 8 }},

Reverse

要素の順序を反転させます。

int[] source = new[] { 1, 2, 3, 4, 5 };
var result = source.Reverse();
// -> 5,4,3,2,1

射影

Select

各要素すべてに処理を行い、その結果を1つのシーケンスとして返します。

int[] source = new[] { 1, 2, 3, 4, 5 };
var result = source.Select(e => e * 2);
// -> 2,4,6,8,10

SelectMany

各要素すべてに処理を行い、その結果を1つのシーケンスに平坦化します。
平坦化とは、例えば List<List<int>> を1つのList<int> にまとめたりすることです。

List<List<int>> lists = new List<List<int>>
{
	new List<int> { 1, 2, 3 },
	new List<int> { 4, 5, 6 },
	new List<int> { 7, 8, 9 },
};
var result = lists.SelectMany(e => e);
// -> 1,2,3,4,5,6,7,8,9

GroupBy

指定のキーで要素をグループ化します。

Pet[] pets = {
	new Pet { Name = "Pochi", Age = 4 },
	new Pet { Name = "Tama", Age = 8 },
	new Pet { Name = "Nyanko", Age = 1 },
	new Pet { Name = "Wanko", Age = 1 }
};
var result = pets.GroupBy(e => e.Age);
// -> {Key = 4, Source = {{ Name = "Pochi", Age = 4 }},
// Key = 8, Source = {{ Name = "Tama", Age = 8 }},
// Key = 1, Source = {{ Name = "Nyanko", Age = 1 }, { Name = "Wanko", Age = 1 }}}

結合

Join

一致するキーに基づいて、2つのシーケンスの要素を結合します。

ItemData[] items = new ItemData[]
{
	new ItemData() { Name = "Pencil", ID = 1 },
	new ItemData() { Name = "Table", ID = 2 },
	new ItemData() { Name = "Chair", ID = 3 },
	new ItemData() { Name = "Lamp", ID = 4 },
};

AmountData[] amounts = new AmountData[]
{
	new AmountData() { ItemID = 2, Amount = 10 },
	new AmountData() { ItemID = 3, Amount = 20 },
};

var result = items.Join(amounts,
					item => item.ID,
					amount => amount.ItemID,
					(item, amount) => new {item.Name, amount.ItemID, amount.Amount});

// -> {{ Name = Table, ItemID = 2, Amount = 10 },
// { Name = Chair, ItemID = 3, Amount = 20 }}

GroupJoin

2つのシーケンスを指定したキーに基づいて結合してグループ化します。

var items = new[]
{
	new { Name = "Pencil", ID = 1 },
	new { Name = "Table", ID = 2 },
	new { Name = "Chair", ID = 3 },
	new { Name = "Lamp", ID = 4 },
};

var amounts = new[]
{
	new { ItemID = 2, Amount = 10 },
	new { ItemID = 3, Amount = 20 },
};

var result = items.GroupJoin(amounts,
					item => item.ID,
					amount => amount.ItemID,
					(i, a) => new { ID = i.ID, Name = i.Name, Amount = a.DefaultIfEmpty() })
					.SelectMany(x => x.Amount, (x, a) => new
					{
						ID = x.ID,
						Name = x.Name,
						Amount = a != null ? a.Amount : -1
					});
// -> {{ ID = 1, Name = Pencil, Amount = -1 },
// { ID = 2, Name = Table, Amount = 10 },
// { ID = 3, Name = Chair, Amount = 20 },
// { ID = 4, Name = Lamp, Amount = -1 }}

Concat

2つのシーケンスを連結します。

var items1 = new[]
{
	new { Name = "Pencil", Amount = 1 },
	new { Name = "Table", Amount = 2 },
	new { Name = "Chair", Amount = 4 },
};

var items2 = new[]
{
	new { Name = "Notebook", Amount = 3 },
	new { Name = "Lamp", Amount = 1 },
};

var result = items1.Concat(items2);
// -> {{ Name = Pencil, Amount = 1 },
// { Name = Table, Amount = 2 },
// { Name = Chair, Amount = 4 },
// { Name = Notebook, Amount = 3 },
// { Name = Lamp, Amount = 1 }}

DefaultIfEmpty

シーケンスを返します。
シーケンスが空の場合は規定値もしくは任意の要素を返します。

var items = new[]
{
	new { Name = "Pencil", Amount = 1 },
	new { Name = "Table", Amount = 2 },
	new { Name = "Chair", Amount = 4 },
};

var result = items.DefaultIfEmpty();
// -> {{ Name = Pencil, Amount = 1 },
// { Name = Table, Amount = 2 },
// { Name = Chair, Amount = 4 }}

Zip

2つのシーケンスに指定した関数を適用し、結果を1つのシーケンスとして生成します。

int[] numbers = new int[] { 1, 2, 3, 4 };
string[] characters = new string[] { "A", "B", "C" };

var result = numbers.Zip(characters, (n, c) => string.Format("{0}-{1}", n, c));
// -> {1-A, 2-B, 3-C}

変換

OfType

要素を指定した型でフィルター処理します。

ArrayList list = new ArrayList { 'A', 2, 7.5, "cat", "dog" };
var result = list.OfType<string>();
// -> { cat, dog }

Cast

要素を指定した型にキャストします。
キャストできない場合は例外をスローします。

ArrayList list1 = new ArrayList { "cat", "dog", "pig" };
var result1 = list1.Cast<string>();
// -> { cat, dog, pig }

ArrayList list2 = new ArrayList { 2, "cat", "dog" };
var result2 = list2.Cast<string>();
// -> InvalidCastException エラー

ToArray

シーケンスから配列を作成します。

ToDictionary

シーケンスから Dictionary<TKey, TValue> を作成します。

ToList

シーケンスから List<T> を作成します。

ToLookup

シーケンスの特定のデータをキーにしたシーケンスにまとめて返します。

AsEnumerable

IEnumerable を返します。

参考資料