1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

.NET LINQ

Last updated at Posted at 2025-06-16

はじめに

絶賛編集中。

LINQとは

.NETはLINQに始まりLINQで終わるのです。(嘘)
やっていきましょう。

サンプルソースに利用するシーケンス

※この異世界では、名前は主キーなんだから。

// 名前と誕生日
record BirthdayRecord(string Name, DateTime Birthday);
// 名前と肩書
record KatagakiRecord(string Name, string Katagaki);

// 誕生日テーブル
var BirthdayTable = new var BirthdayTable[]{ ... };
// 肩書テーブル
var KatagakiTable = new var BirthdayTable[]{ ... };

クエリ演算子一覧

System.Linq.Enumerableのメソッドです。
.NET9時点になります。

用途 メソッド
集計 AggregateAggregateByAverageCountCountByLongCountMaxMaxByMinMinBySum
判定 AllAnySequenceEqual
レコード足す AppendPrepend
分割・結合 ChunkConcatSkipSkipLastSkipWhileTakeTakeLastTakeWhile
CastOftype
デフォルト DefaultIfEmptyElementAtOrDefaultFirstOrDefaultSingleOrDefault
重複削除 DistinctDistinctBy
レコード取得 ElementAtElementAtOrDefaultFirstFirstOrDefaultLastLastOrDefaultSingleSingleOrDefault
集合 ExceptExceptByIntersectIntersectByUnionUnionBy
グルーピング CountByGroupByGroupJoin
テーブル連結 GroupJoinJoinZip
順序 OrderOrderByOrderDescendingOrderByDescendingReverseThenByThenByDescending
セレクト SelectSelectMany
フィルタ OftypeSingleSingleOrDefaultWhere
変換 IndexToArrayToDictionaryToHashSetToListToLookupZip
その他 AsEnumerableEmptyRangeRepeatTryGetNonEnumeratedCount

Aggregate

集計

一番若人を探す。

var retAggregate = BirthdayTable.Aggregate((youngest, target) => youngest.Birthday > target.Birthday ? youngest : target);
System.Console.WriteLine($"retAggregate = {retAggregate.Name}"); // りんく太郎

AggregateBy

GroupBy+Aggregate

年代毎の人数を集計する。

var retAggregateBy = BirthdayTable.AggregateBy(r => r.Birthday.Year / 10 * 10, 0, (count, r) => ++count).OrderBy(r=>r.Key);
System.Console.WriteLine($"retAggregateBy = ");
foreach(var r in retAggregateBy)
{
    System.Console.WriteLine($"  {r.Key}年代 = {r.Value}人");  // 1990年代 49人
}

All

全てTrue

若人Top10は、全員2000年代生まれか?

var YoungestTop10 = BirthdayTable.OrderByDescending(r => r.Birthday).Take(10);
var retAll = YoungestTop10.All(r => r.Birthday.Year >= 2000);
System.Console.WriteLine($"retAll = {retAll}"); // True or False

Any

一部True

若人Top10に、2000年より前の生まれの人がいるか?

// var YoungestTop10 = BirthdayTable.OrderByDescending(r => r.Birthday).Take(10);
var retAny = YoungestTop10.Any(r => r.Birthday.Year < 2000);
System.Console.WriteLine($"retAny = {retAny}"); // True or False

Append

最後に足す

var retAppend = new[] { 1, 2, 3, 4 }.Append(5);
System.Console.WriteLine($"retAppend = {string.Join(",", retAppend)}"); // 1,2,3,4,5

Average

平均

平均年齢は?

// 誕生日から年齢を算出する
int CalcAge(DateTime birthday)
{
    var today = DateTime.Today;
    int age = today.Year - birthday.Year;
    if (today < birthday.AddYears(age))
    {
        age--;
    }
    return age;
}

var AgeTable = BirthdayTable.Select(r => new { Name=r.Name, Age=CalcAge(r.Birthday)});
var retAverage = AgeTable.Select(r=>r.Age).Average();
System.Console.WriteLine($"retAverage = {retAverage:0.00}"); // 42.22

Cast

キャスト

var objectArray = new object[] { 1, 2, 3 };
// System.Console.WriteLine($"retCast = {objectArray.Average()}"); ※コンパイルエラー
var retCast = objectArray.Cast<int>();
System.Console.WriteLine($"retCast = {retCast.Average()}"); // 2

Chunk

分割

var intArray = new int[] { 1, 2, 3, 4, 5 };
var retChunk = intArray.Chunk(3).ToArray();
System.Console.WriteLine($"retChunk.Length = {retChunk.Length}, retChunk[0]={string.Join(",", retChunk[0])}, retChunk[1]={string.Join(",", retChunk[1])} ");
// retChunk.Length = 2, retChunk[0]=1,2,3, retChunk[1]=4,5

Concat

連結

// var intArray = new int[] { 1, 2, 3, 4, 5 };
// var retChunk = intArray.Chunk(3).ToArray();
var retConcat = retChunk[0].Concat(retChunk[1]);
System.Console.WriteLine($"retConcat = {string.Join(",", retConcat.ToArray())}"); // 1,2,3,4,5

Count

カウント

課長は何人?

var retCount = KatagakiTable.Where(r=>r.Katagaki == "課長").Count();
System.Console.WriteLine($"retCount = {retCount}人"); // 10人

CountBy

GroupBy+Count

肩書ごとの人数を数える。

var retCountBy = KatagakiTable.CountBy(r => r.Katagaki);
System.Console.WriteLine($"retCountBy = ");
foreach (var r in retCountBy)
{
    System.Console.WriteLine($"  {r.Key} = {r.Value}人"); // 課長 = 10人
}

DefaultIfEmpty

デフォルト

var EmptyTable = new List<BirthdayRecord>();
// var retDefaultIfEmpty = EmptyTable.First();  ※System.InvalidOperationException: 'Sequence contains no elements'
var retDefaultIfEmpty = EmptyTable.DefaultIfEmpty(null).First();
System.Console.WriteLine($"retDefaultIfEmpty = {retDefaultIfEmpty}"); // 空文字(null)

Distinct

重複削除

var DuplicateTable = new[] { 1, 2, 3, 2, 3, 4, 1 };
var retDistinct = DuplicateTable.Distinct();
System.Console.WriteLine($"retDistinct = {string.Join(",", retDistinct)}");

DistinctBy

キーセレクタDistinct

肩書毎に先頭レコードが選ばれる。

var retDistinctBt = KatagakiTable.DistinctBy(r=>r.Katagaki);
System.Console.WriteLine($"retDistinctBt = {string.Join(",", retDistinctBt)}");

ElementAt

指定位置の要素

//var intArray = new int[] { 1, 2, 3, 4, 5 };
var retElementAt = intArray.ElementAt(3);
System.Console.WriteLine($"retElementAt = {retElementAt}"); // 4

ElementAtOrDefault

DefaultIfEmpty+ElementAt

//var intArray = new int[] { 1, 2, 3, 4, 5 };
// var retElementAt = intArray.ElementAt(5); ※System.ArgumentOutOfRangeException: 'Index was out of range. Must be non-negative and less than the size of the collection. 
var retElementAtOrDefault = intArray.DefaultIfEmpty(0).ElementAtOrDefault(5);
System.Console.WriteLine($"retElementAtOrDefault = {retElementAtOrDefault}"); // 0

Except

差集合

var tableA = Enumerable.Range(1, 10); // 1~10
var tableB = Enumerable.Range(5, 10); // 5~15
var retExcept = tableA.Except(tableB);
System.Console.WriteLine($"retExcept = {string.Join(",", retExcept)}"); // 1,2,3,4
var retExcept2 = tableB.Except(tableA);
System.Console.WriteLine($"retExcept2 = {string.Join(",", retExcept2)}"); //11,12,13,14

ExceptBy

キーセレクタExcept

社長、部長、課長以外の肩書をもつ先頭レコードを抽出する。

var retExceptBy = KatagakiTable.ExceptBy(new[] { "社長", "部長", "課長" }, r => r.Katagaki);
System.Console.WriteLine($"retExceptBy = {string.Join(",", retExceptBy)}");

First

先頭

var retFirst = KatagakiTable.First();
System.Console.WriteLine($"retFirst = {retFirst}"); // 先頭のレコード

FirstOrDefault

DefaultEmpty+First

var emptyRecord = new BirthdayRecord(string.Empty, DateTime.MinValue);
var retFirstOrDefault = EmptyTable.FirstOrDefault(emptyRecord);
System.Console.WriteLine($"retFirstOrDefault = {retFirstOrDefault}"); // emptyRecord

GroupBy

グルーピング

肩書毎の名前を一覧する

var retGroupBy = KatagakiTable.GroupBy(r=>r.Katagaki);
Console.WriteLine($"retGroupBy = ");
foreach(var group in retGroupBy)
{
    Console.WriteLine($"  {group.Key} {string.Join(",", group.Select(r => r.Name))}"); // 肩書 社員A,社員B...
}

GroupJoin

Join+GroupBy

1:N関係のテーブルをJoinしてGroupBy

var OuterTable = new Tuple<int, string>[] { new(1, "りんく太郎"), new(2, "りんく次郎") };
var InnerTable = new Tuple<int, string>[] { new(1, "C#"), new(1, "LINQ") };
var retGroupJoin = OuterTable.GroupJoin(InnerTable, o => o.Item1, i => i.Item1, (o, i) => new { Outer = o, Inner = i.ToList() });
Console.WriteLine($"retGroupJoin = ");
foreach (var r in retGroupJoin)
{
    Console.WriteLine($"  {r.Outer.Item2} {string.Join(",", r.Inner.Select(r=>r.Item2))}");
}
//retGroupJoin =
//  りんく太郎 C#,LINQ
//  りんく次郎

Index

連番

var retIndex = BirthdayTable.Index();
Console.WriteLine($"retIndex = ");
foreach (var r in retIndex)
{
    Console.WriteLine($"  {r.Index} {r.Item.Name}");  // 1 りんく太郎
}

Intersect

積集合

// var tableA = Enumerable.Range(1, 10); // 1~10
// var tableB = Enumerable.Range(5, 10); // 5~15
var retIntersect = tableA.Intersect(tableB);
Console.WriteLine($"retIntersect = {string.Join(",", retIntersect)}"); // 5,6,7,8,9,10

IntersectBy キーセレクタIntersect

課長の肩書をもつ先頭レコードを抽出する。

var retIntersectBy = KatagakiTable.IntersectBy(new[] { "課長" }, r => r.Katagaki);
Console.WriteLine($"retIntersectBy = {string.Join(",", retIntersectBy)}");

Join

連結

var retJoin = KatagakiTable.Join(BirthdayTable, o => o.Name, i => i.Name, (o, i) => new { Outer = o, Inner = i });
Console.WriteLine($"retJoin = ");
foreach (var r in retJoin)
{
    Console.WriteLine($"  {r.Outer.Name} {r.Outer.Katagaki} {r.Inner.Birthday:yyyy/MM/dd}"); // りんく次郎 プログラマ 2007/11/19
}

Last

最後

var retLast = KatagakiTable.Last();
Console.WriteLine($"retLast = {retLast}"); // 最後のレコード

LastOrDefault DefaultIfEmpty+Last

//var emptyRecord = new BirthdayRecord(string.Empty, DateTime.MinValue);
var retLastOrDefault = EmptyTable.LastOrDefault(emptyRecord);
Console.WriteLine($"retLastOrDefault = {retLastOrDefault}"); // emptyRecord

LongCount

カウント(long型)

全部で何人?

var retLongCount = KatagakiTable.LongCount();
System.Console.WriteLine($"retLongCount = {retLongCount}人"); // 253人

Max

最大

//var AgeTable = BirthdayTable.Select(r => new { Name = r.Name, Age = CalcAge(r.Birthday) });
var retMax = AgeTable.Select(r => r.Age).Max();
System.Console.WriteLine($"retMax = {retMax:0.00}"); // 65.00

MaxBy

キーセレクタMax

//var AgeTable = BirthdayTable.Select(r => new { Name = r.Name, Age = CalcAge(r.Birthday) });
var retMaxBy = AgeTable.MaxBy(r => r.Age);
System.Console.WriteLine($"retMaxBy = {retMaxBy.Age:0.00}"); // 65.00

Min

最小

//var AgeTable = BirthdayTable.Select(r => new { Name = r.Name, Age = CalcAge(r.Birthday) });
var retMin = AgeTable.Select(r => r.Age).Min();
System.Console.WriteLine($"retMin = {retMin:0.00}"); // 17.00

MinBy

キーセレクタMin

//var AgeTable = BirthdayTable.Select(r => new { Name = r.Name, Age = CalcAge(r.Birthday) });
var retMinBy = AgeTable.MinBy(r => r.Age);
System.Console.WriteLine($"retMinBy = {retMinBy.Age:0.00}"); // 17.00

OfType

型でフィルタ

var objectArray2 = new object[] { 2.4, "a", 1, 3.5 };
var retOfType = objectArray2.OfType<Double>();
System.Console.WriteLine($"retOfType = {string.Join(",", retOfType)}"); // 2.4,3.5

Order

昇順

var randomArray = new[] { 5, 2, 3, 1, 4 };
var retOrder = randomArray.Order();
System.Console.WriteLine($"retOrder = {string.Join(",", retOrder)}"); // 1,2,3,4,5

OrderBy

キーセレクタOrder

// BirthdayRecordの昇順
// var retOrder = BirthdayTable.Order().ToList(); System.InvalidOperationException: 'Failed to compare two elements in the array.'
// 誕生日で昇順
var retOrderBy = BirthdayTable.OrderBy(r=>r.Birthday).ToList();

OrderDescending

降順

// var randomArray = new[] { 5, 2, 3, 1, 4 };
var retOrderDescending = randomArray.OrderDescending();
System.Console.WriteLine($"retOrderDescending = {string.Join(",", retOrderDescending)}"); // 5,4,3,2,1

OrderByDescending

キーセレクタOrderDescending

// BirthdayRecordの降順
// var retOrderDescending = BirthdayTable.OrderDescending().ToList(); System.InvalidOperationException: 'Failed to compare two elements in the array.'
// 名前の降順
var retOrderByDescending = BirthdayTable.OrderByDescending(r=>r.Name).ToList();

Prepend

先頭に足す

var retPrepend = new[] { 1, 2, 3, 4 }.Prepend(5);
Console.WriteLine($"retPrepend = {string.Join(",", retPrepend)}"); // 5,1,3,4

Reverse

反転

var retReverse = new[] { 1, 2, 3, 4 }.Reverse();
Console.WriteLine($"retReverse = {string.Join(",", retReverse)}"); // 4,3,2,1

Select

セレクト(1⇒1)

誕生日テーブルから誕生年を取得する

var retSelect = BirthdayTable.Select(r=>r.Birthday.Year).Distinct();
Console.WriteLine($"retSelect = {string.Join(",", retSelect)}");

SelectMany

セレクト(1⇒N)

名前に使われている文字を取得する

var retSelectMany = BirthdayTable.SelectMany(r => r.Name.ToList()).Distinct();
Console.WriteLine($"retSelectMany = {string.Join(",", retSelectMany)}");

SequenceEqual

等値

//var tableA = Enumerable.Range(1, 10);
//var tableB = Enumerable.Range(5, 10);
var tableC = Enumerable.Range(5, 10).ToList();
var retSequenceEqual = tableA.SequenceEqual(tableB);
Console.WriteLine($"retSequenceEqual = {retSequenceEqual}"); // False
var retSequenceEqual2 = tableC.SequenceEqual(tableB);
Console.WriteLine($"retSequenceEqual2 = {retSequenceEqual2}"); // True

Single

一意にするフィルタ

// 指定された生年月日の人がいない
// var retSingle = BirthdayTable.Single(r => r.Birthday == new DateTime(9999, 12, 31));  System.InvalidOperationException: 'Sequence contains no matching element'

// 同じ生年月日の人がいる
// var retSingle = BirthdayTable.Single(r => r.Birthday == new DateTime(2002, 10, 10)); System.InvalidOperationException: 'Sequence contains more than one matching element'

var retSingle = BirthdayTable.Single(r => r.Birthday == new DateTime(2007, 11, 19));
Console.WriteLine($"retSingle = {retSingle.Name}"); // りんく次郎

SingleOrDefault

DefaultIfEmpty+Single

var retSingleOrDefault = BirthdayTable.SingleOrDefault(r => r.Birthday == new DateTime(9999, 12, 31), new BirthdayRecord(string.Empty, DateTime.MinValue));
    Console.WriteLine($"retSingleOrDefault = {retSingleOrDefault}"); // { Name = , Birthday = 0001/01/01 0:00:00 }

Skip

前方スキップ

//var tableA = Enumerable.Range(1, 10);
var retSkip = tableA.Skip(5);
Console.WriteLine($"retSkip = {string.Join(",", retSkip)}"); // 6,7,8,9,10

SkipLast

後方スキップ

//var tableA = Enumerable.Range(1, 10);
var retSkipLast = tableA.SkipLast(5);
Console.WriteLine($"retSkipLast = {string.Join(",", retSkipLast)}"); // 1,2,3,4,5

SkipWhile

条件満たす間スキップ

//var tableA = Enumerable.Range(1, 10);
var retSkipWhile = tableA.SkipWhile(r=>r < 8);
Console.WriteLine($"retSkipWhile = {string.Join(",", retSkipWhile)}"); // 8, 9, 1

Sum

合計


Take

先頭から指定数の要素を取得


TakeLast

後方から指定数の要素を取得


TakeWhile

先頭から条件を満たす要素を取得


ThenBy

昇順(複数列)


ThenByDescending

降順(複数列)


ToArray

変換


ToDictionary

変換


ToHashSet

変換


ToList

変換


ToLookup

変換


Union

和集合


UnionBy

条件指定Union


Where

フィルタ


Zip

順番通りに連結


その他

AsEnumerable

LINQのメソッドと同名の独自メソッドを隠蔽するために利用する。

Empty

空のシーケンスを作成する。

var EmptyTable = Enumerable.Empty<BirthdayRecord>();

Range

整数シーケンスを作成する。

var RangeTable = Enumerable.Range(2, 10); // {2,3,4,5,6,7,8,9,10,11}

Repeat

同値シーケンスを作成する。

var RepeatTable = Enumerable.Repeat("hoge", 3);  // {"hoge","hoge","hoge"}

TryGetNonEnumeratedCount

Enumeratable.Count()を使わずにカウントできればカウントする。

クエリ式

from ~ in ~

where ~

orderby ~

group ~ by ~

group ~ by ~ into ~

join ~ in ~ on ~ equals ~

select ~

select new {}

let ~

サブクエリ

Parallel LINQ (PLINQ)

前に書いた記事を載せとく。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?