C# LINQ サンプル集

# はじめに
LINQで遊ぶならLINQPad推奨。

※分類はなんとなくで決めてる


# 選択(フィルタ)

## Where (filter, selection)

```csharp
int[] nums2 = new[] { 1, 2, 3, 4, 5 };
IEnumerable<int> evenNums = nums2.Where(x => x % 2 == 0); 
// -> { 2, 4 }
```

## Whereの省略

```csharp
var nums = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

// Where したあと Count する代わりに、
nums.Where(_ => _ % 2 == 0).Count();

// Count に直接 predicate を指定できる
nums.Count(_ => _ % 2 == 0);

// First や Last も同様
nums.First(_ => _ % 2 == 0);
nums.Last(_ => _ % 2 == 0);
```

# 射影

## Select (map, projection)
```csharp
string[] nums = new[] { "1", "2", "3", "4", "5" };
IEnumerable<int> numsInt = nums.Select(x => Int32.Parse(x));
// -> { 1, 2, 3, 4, 5 }
```

## SelectMany
```csharp
string[][] jaggedArray =
{
    new []{"a", "b", "c"},
    new []{"1", "2", "3"},
    new []{"い", "ろ", "は"},
};

IEnumerable<string> flatten = jaggedArray.SelectMany(x => x);
// -> {"a", "b", "c", "1", "2", "3", "い", "ろ", "は"}
```

### 参考：こんな風に使えるかも
```csharp
// ローテートされた複数のログファイルをまとめて1つの配列に落とすサンプル
var logFiles = new[]
{
    @"C:\Logs\access.log",
    @"C:\Logs\access.log.1",
    @"C:\Logs\access.log.2",
};
var logLines = logFiles.SelectMany(file => File.ReadAllLines(file));
```

# 結合

## Join, GroupJoin

```csharp
var staffs = new[]
{
	new {StaffId = 1, DepartmentId = 1, Name = "Tanaka"},
	new {StaffId = 2, DepartmentId = 3, Name = "Suzuki"},
	new {StaffId = 3, DepartmentId = 2, Name = "Takahashi"},
	new {StaffId = 4, DepartmentId = 1, Name = "Sato"},
};
var departments = new[]
{
	new {DepartmentId = 1, Name = "Personnel"},
	new {DepartmentId = 2, Name = "Accounting"},
	new {DepartmentId = 3, Name = "General Affairs"},
};
var staffname_and_department =
        staffs.Join(
            departments,
            staff => staff.DepartmentId,
            department => department.DepartmentId,
            (staff, department) => new { Name = staff.Name, Department = department.Name });
var staffnames_for_department =
        departments.GroupJoin(
            staffs,
            department => department.DepartmentId,
            staff => staff.DepartmentId,
            (department, staff) => new { Department = department, Staffs = staff });
```

staffname_and_department (Join)

![join.png](https://qiita-image-store.s3.amazonaws.com/0/118451/b133df79-4bab-f86f-e514-ea3da1629100.png)


staffnames_for_department (GroupJoin)

![groupjoin.png](https://qiita-image-store.s3.amazonaws.com/0/118451/0575f992-c41f-1214-e679-0c2c71c7b8d8.png)


# グループ化

## GroupBy
```csharp
var employees = new[]
{
	new {Name = "Tanaka", Gender = "Male"},
	new {Name = "Suzuki", Gender = "Female"},
	new {Name = "Sato", Gender = "Male"},
	new {Name = "Tanaka", Gender = "Male"},
	new {Name = "Tanaka", Gender = "Female"},
};
var danjosuu = employees
               .GroupBy(e => e.Gender)
               .Select(g => new { Gender = g.Key, Count = g.Count() });
```

danjosuu (GroupBy)

|Gender|Count|
|:--|:--|
|Male|3|
|Female|2|

# 要素を含んでるかを確かめる

## All：要素すべてが条件に一致する
```csharp
var numarray2 = new[] { 2, 4, 6 };

bool isAllEven = numarray2.All(x => x % 2 == 0); // true
```

## Any：空ではない または 要素どれかが条件に一致する
```csharp
var numarray3 = new[] { 1, 2, 3, 5, 7, 9 };

bool isNotEmpty = numarray3.Any(); // true

bool hasEvenNumber = numarray3.Any(x => x % 2 == 0); // true
```

## Contains：含む
```csharp
var numarray4 = new[] { 1, 4, 7, 10 };
bool contains4 = numarray4.Contains(4); // true
```


# 並びかえ

## OrderBy, ThenBy, Reverse
```csharp
var staffs2 = new[]
{
	new {Name = "Suzuki", Level = 10, Age = 19},
	new {Name = "Tanaka", Level = 5, Age = 27},
	new {Name = "Sato", Level = 10, Age = 32},
	new {Name = "Takahashi", Level = 3, Age = 30},
	new {Name = "Tamura", Level = 10, Age = 23},
};

staffs2.OrderByDescending(staff => staff.Level)// レベルが高い順
       .ThenBy(staff => staff.Age) // 次に若い順
       .Reverse(); // 逆順もできる
```

# １つだけとりだす

## First, Last, ElementAt
```csharp
var nums3 = new[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int zero = nums3.First();
int nine = nums3.Last();
int five = nums3.ElementAt(5);
```

## Single
```csharp
var nums4 = new[] { 4 };
int four = nums4.Single();
```

## DefaultIfEmpty, ~~OrDefault
```csharp
var emptyArray = new string[] {};

//emptyArray.First(); // throws exception
emptyArray.FirstOrDefault(); // returns null
emptyArray.DefaultIfEmpty("default").FirstOrDefault(); // returns "default"
```


# 1部だけとりだす

## Skip, Take
```csharp
var nums5 = new[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
var three2five = nums5.Skip(3).Take(3);
// -> { 3, 4, 5 }
```

## SkipWhile, TakeWhile
```csharp
var schedule = new[]
{
    new { Date = new DateTime(2017, 4, 11), IsWorkDay = true},
    new { Date = new DateTime(2017, 4, 12), IsWorkDay = true},
    new { Date = new DateTime(2017, 4, 13), IsWorkDay = true},
    new { Date = new DateTime(2017, 4, 14), IsWorkDay = true},
    new { Date = new DateTime(2017, 4, 15), IsWorkDay = false},
    new { Date = new DateTime(2017, 4, 16), IsWorkDay = false},
    new { Date = new DateTime(2017, 4, 17), IsWorkDay = true},
};

var tugihananrenkyuu = schedule
                       .SkipWhile(day => day.IsWorkDay)
                       .TakeWhile(day => !day.IsWorkDay)
                       .Count(); // 2
```

# ほかのコレクションと何かする系

## Concat：連結
```csharp
string[] namesA = new[] { "Tanaka", "Suzuki" };
string[] namesB = new[] { "Sato", "Takahashi" };

IEnumerable<string> nameAB = namesA.Concat(namesB);
// -> { "Tanaka", "Suzuki", "Sato", "Takahashi" }
```

## Zip：2つのシーケンスを同時に扱う
```csharp
var idlist = new[] { 3, 2, 1 };
var namelist = new[] { "Tanaka", "Suzuki", "Sato" };
var idnames = idlist.Zip(namelist, (id, name) => new { Id = id, Name = name });
```

idnames (Zip)

|Id|Name|
|:--|:--|
|3|Tanaka|
|2|Suzuki|
|1|Sato|

## SequentialEqual：同じ順番？
```csharp
var numarray = new[] { 1, 2, 3, 4, 5 };
var evenarray = new[] { 2, 4 };
bool sequentialEqual = numarray.Where(x => x % 2 == 0).SequenceEqual(evenarray); // true
```



# 集計

## Sum, Max, Min, Average, Count, LongCount, Aggregate (fold, reduce)
```csharp
var nums6 = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
nums6.Sum(); // 55
nums6.Max(); // 10
nums6.Min(); // 1
nums6.Average(); // 5.5
nums6.Count(); // 10
nums6.LongCount(); // 10

// Example: ((((((((1 * 2) * 3) * 4) * 5) * 6) * 7) * 8) * 9) * 10)
nums6.Aggregate((x, y) => x * y); // 3628800 
```


# 集合
## Distinct, Union, Intersect, Except
```csharp
var primes = new[] { 2, 3, 5, 7, 11, 13 };
var oddNums = new[] { 1, 1, 3, 3, 5, 7, 9, 11, 13 };

var uniqueOdd = oddNums.Distinct();
// -> { 1, 3, 5, 7, 9, 11, 13 }

var primeOrOdd = primes.Union(oddNums);
// -> { 2, 3, 5, 7, 11, 13, 1, 9 }

var primeAndOdd = primes.Intersect(oddNums);
// -> { 3, 5, 7, 11, 13 }

var noPrimeOdd = oddNums.Except(primes);
// -> { 1, 9 }
```


# 変換

## ToArray, ToList
```csharp
var nums7 = new[] { 1, 2, 3, 4, 5, 6 };
int[] evenArray = nums7.Where(x => x % 2 == 0).ToArray();
List<int> oddList = nums7.Where(x => x % 2 != 0).ToList();
```

## ToDictionary, ToLookup
```csharp
var staffs3 = new[]
{
	new {StaffId = 1, DepartmentId = 1, Name = "Tanaka"},
	new {StaffId = 2, DepartmentId = 3, Name = "Suzuki"},
	new {StaffId = 3, DepartmentId = 2, Name = "Takahashi"},
	new {StaffId = 4, DepartmentId = 1, Name = "Sato"},
};
var id2name = staffs3.ToDictionary(staff => staff.StaffId, staff => staff.Name);
var depid2staff = staffs3.ToLookup(staff => staff.DepartmentId);
```
id2name (ToDictionary)

![todict.png](https://qiita-image-store.s3.amazonaws.com/0/118451/5d89547d-750d-3776-8d9c-a7267d8176f3.png)


depid2staff (ToLookup)

![tolook.png](https://qiita-image-store.s3.amazonaws.com/0/118451/28eee44a-d08f-8ebc-1abd-795beb8b94a1.png)

 
### ToLookup と GroupBy の違い
ToLookupは即時評価される。GroupByは遅延評価。

## Cast, OfType
```csharp
ArrayList alist = new ArrayList() { "First", "Second", null, "Third" };
string[] arraycast = alist.Cast<string>().ToArray();
// -> { "First", "Second", null, "Third" }
string[] arrayoftype = alist.OfType<string>().ToArray();
// -> { "First", "Second", "Third" }
```

### Cast と OfType の違い

- Castはキャストに失敗した場合、例外をスローする。nullは残る。
- OfTypeはキャスト可能な要素のみ取得する。nullは残らない。