LINQ.Enumerable

概要

System.Linq.Enumerableクラス
・IEnumerableインターフェイスを継承している。
・拡張メソッドSumを始め、様々なメソッドを定義している。
※拡張メソッドについては「オブジェクト指向: 特殊なクラス/拡張メソッド」を参照。

public static class System.Linq.Enumerable
{
 public static int Sum(this IEnumerable<int> source);
}
拡張メソッドの定義の結果、ary.Sum() が可能
※aryはIEnumerable<int>型の変数
IEnumerable<int>型の変数を宣言するには、
var
を用いる。
なお、IEnumerableインターフェイスを継承したオブジェクトの操作は、
LINQ to Objectに分類される。
・LINQ to SQL (データベースオブジェクト:Entity Frameworkに対する操作)
・LINQ to XML

IEnumerableインターフェイス拡張メソッド

合計
var people = new[]{
  new { Name="一郎", Height=160, Weight=50 },
  new { Name="二郎", Height=170, Weight=60 },
  new { Name="三郎", Height=180, Weight=70 }
};
var WeightSum = people.Sum(person => person.Weight);
// ⇒ Weightsum : 180


int[] numbers = new int[] { 0, 1, 2, 3, 4, 5 };
int count = numbers.Count();
// count : 6

最大値
int max = numbers.Max();
// max : 5

//最大の(身長-体重)を求める。
var judgeValue = people.Max(person => person.Height – person.Weight);
// judgeValue : 110

最小値
int min = numbers.Min();
// min : 0

平均値
double avr = numbers.Average();
// avr : 2.5

逐次処理

each・foreach

var ary = new[] { 1, 2, 3, 4, 5 };
foreach(var i in ary)
{
  Console.WriteLine(i);
  ⇒ 0 1 2 3 4 5
}

プログラム言語 IEnumerableインターフェイス継承クラスの自作」参照

抽出

写像(map・select)

var ary = new[] { 1, 2, 3, 4, 5 };
var map = ary.Select(elm => elm * 2);

OfType
foreach (var elm in myObj.Select(n => n is string)) Console.Write(elm);
foreach (var elm in myObj.OfType<string>()) Console.Write(elm);

var list = new List<string>();
list.AddRange(myObj.OfType<string>());

フィルター(filter・where)

var ary = new[] { 1, 2, 3, 4, 5 };
var map = ary.Where(elm => elm == 2);

差分・共通項・重複削除

Except
var allAry = Enumerable.Range(start: 1, count: 10);
int[] exceptAry = { 3, 7 };
var newAry = allAry.Except(second: exceptAry).ToArray();
差分を取得
newAry:1 2 4 5 6 8 9 10

Intersect
int[] ary1 = { 1, 3, 4, 5, 7, 9 };
int[] ary2 = { 2, 3, 5, 6, 9 };
var newAry = ary1.Intersect(second: ary2).ToArray();
共通値を取得
newAry:3 5 9

Union
int[] ary1 = { 1, 2, 3, 3 };
int[] ary2 = { 2, 3, 4, 4 };
var newAry = ary1.Union(second: ary2).OrderBy(_ => _).ToArray();
→1, 2, 3, 4
※重複も削除される

Distinct
int[] ary = { 0, 1, 1, 2, 3, 3, 4, 5, 5 };
var distinct = ary.Distinct();
拡張子の重複を削除して抽出
⇒ distinct : 0 1 2 3 4 5

string[] oldAry = { "a.txt", "b.txt", "c.jpg", "d.doc", "e.txt" };
string[] newAry = oldAry.Select(x => Path.GetExtension(x)).Distinct().ToArray();
newAry:txt jpg doc

指定の要素

ElementAt/ElementAtOrDefault
var rnd = new Random(Seed: 0);
var ary = Enumerable.Repeat(element: 0, count: 10000).Select(_ => rnd.Next());
var target1 = ary.ElementAt(index: 4649);
var target2 = ary.ElementAtOrDefault(index: 4649);

Take/Skip
int[] ary = { 1, 2, 3, 4, 5 };
ary = ary.Take(3).Concat(new int[] { 0 }).Concat(ary.Skip(3)).ToArray();
→1, 2, 3, 0, 4, 5

SkipWhile/TakeWhile
int[] myInt = Enumerable.Range(start: 1, count: 100).ToArray();
var ary = myInt.SkipWhile(n => n < 10).TakeWhile(n => n < 21);
ary:10~20

int sum = 0;
foreach (var n in Enumerable.Range(1, int.MaxValue).TakeWhile(c => sum < 100))
{
 sum += 1;
 Console.WriteLine(sum);
} 
条件が成立する間は実行

First
※対象が無い場合:例外発生
FirstOrDefault
※対象が無い場合:型のデフォルト値(0,null等)
Last
※対象が無い場合:例外発生
LastOrDefault
※対象が無い場合:型のデフォルト値(0,null等)
int?[] ary = { 0, 1, 2 };
var my1st = ary.First(n => n < 2);
var my1st = ary.FirstOrDefault(n => n < 2);
var my1st = ary.Last(n => n < 2);
var my1st = ary.LastOrDefault(n => n < 2);

int?[] ary = { 0, 1, 2 };
var my1st = ary.FirstOrDefault(n => n > 2) ?? -1;

→Null → -1

Single / SingleOrDefault
int[] ary1 = { 2 };
int[] ary2 = { };
int[] ary3 = { 2, 3 };

int sglValue1 = ary1.Single();
→2
int sglValue2 = ary2.Single();
→実行時例外
int sglValue2 = ary2.SingleOrDefault();
→0
int sglValue3 = ary3.SingleOrDefault();
→実行時例外

DefaultIfEmpty
string[] ary = { };
foreach (var elm in ary.DefaultIfEmpty(defaultValue: @"ありません。")) Console.WriteLine(elm);
要素数:0 の場合の値を指定(同型の値のみ)

キャスト

object[] obj = { @"A", @"B", @"C" };
string[] str = obj.Cast<string>().ToArray();

int[] myInt = { 1, 2, 3 };
long[] myLong = myInt.Cast<long>().ToArray();
→エラー:Castは参照型にしか使用できない

long[] myLong = myInt.Select(n => (long)n).ToArray();

順序

並び替え(sort)

OrderBy
var orderBy = people.OrderBy(person => person.Height);
OrderByDecending
var orderByDec = people.OrderByDescending(person => person.Height);

DateTime[] ary = new DateTime[]
{
 new DateTime(year: 2010, month: 1, day: 1),
 new DateTime(year: 2001, month: 1, day: 1),
 new DateTime(year: 2005, month: 1, day: 1),
 new DateTime(year: 2003, month: 1, day: 1)
};
var newary1 = ary.OrderBy(n => n);
=Array.Sort(ary, (x, y) => Math.Sign(x.Ticks – y.Ticks));

ThenBy
var ThenBy = people.OrderBy(person => person.Height).ThenBy(person => person.Weight);
ThenByDecending
var ThenByDec = people.OrderBy(person => person.Height).ThenByDescending(person => person.Weight);

Sort
var list = new List<DateTime>() {
 DateTime.Now, DateTime.MinValue, DateTime.MaxValue };
list.Sort((x, y) => Math.Sign(x.Ticks – y.Ticks));
list.ForEach( c => Console.WriteLine(c) );

反転

var reverse = distinct.Reverse();
// ⇒ 5 4 3 2 1 0

判定

ALL・ANY

var ary = new[] { 1, 2, 3, 4, 5 };

bool judge = ary.All( elm => elm > 3);
全ての要素が条件を満たすか否か?

bool judge = ary.Any( elm => elm > 3);
どれか1つの要素が条件を満たすか否か?

int[] ary1 = { 1, 3, 4, 5, 7, 9 };
int[] ary2 = { 2, 3, 5, 6, 9 };
int[] ary3 = { 1, 3, 4, 5, 7, 9 };
bool judge2 = ary1.SequenceEqual(second: ary2);
judge2:false
bool judge3 = ary1.SequenceEqual(second: ary3);
judge3:true
全ての要素が一致するか?

Contains

var isContain = numbers.Contains(value: 1);
// isContain : true

配列・シーケンス(列挙オブジェクト)作成

シーケンス(列挙オブジェクト)
IEnumerable<T>型変数

連結

配列とリスト等、型が違っても連結可能
List<int> myList = new List<int> { 0,0,0,0,0 };
var concat = numbers.Concat(second: myList);
⇒ 0 1 2 3 4 5 0 0 0 0 0
範囲・繰り返し

var range = Enumerable.Range(start: 1, count: 10);
range : 1 2 3 4 5 6 7 8 9 10

var repeat = Enumerable.Repeat(element: "a", count: 5);
repeat : "a" "a" "a" "a" "a"
文字列 "aaaaa"ではない。配列。

空のシーケンス(列挙オブジェクト)作成

※シーケンス(列挙オブジェクト)≠配列。
IEnumerableを継承したオブジェクト

var ary1 = (IEnumerable<int>)new int[0];
int cnt1 = ary1.Count();
cnt1:0

var ary2 = Enumerable.Empty<int>();
int cnt2 = ary2.Count();
cnt2:0

テキストファイルから

行単位で配列に格納(即時実行)
string[] s1 = File.ReadAllLines(@"D:\test.txt", Encoding.Default);

行単位で列挙オブジェクトに格納(遅延実行)
var s2 = File.ReadLines(@"D:\test.txt", Encoding.Default);

条件に合う行を列挙オブジェクトに格納(遅延実行)
var s3 = File.ReadLines(@"D:\test.txt", Encoding.Default).Where(x => x.Equals(@"test1"));

条件に合う行数を取得(遅延実行)
int cnt = File.ReadLines(@"D:\test.txt", Encoding.Default).Count(x => x.ToString().Contains(@"test"));

列挙オブジェクトの書込
static IEnumerable<string> returnString()
{
 yield return @"test1";
 yield return @"test2";
}
File.WriteAllLines(@"D:\test.txt", returnString());

File.WriteAllLines(@"D:\test.txt", Enumerable.Range(~));

ファイル情報

using System.IO;

var files = Directory.EnumerateFiles(
 path: @"D:\a", 検索開始ディレクトリ
 searchPattern: "?0.txt", 検索パターン
 searchOption: SearchOption.AllDirectories);

SearchOption.TopDirectoryOnly 指定ディレクトのみ
SearchOption.AllDirectories サブ・ディレクトも含める

files.ToList().ForEach(x => Console.WriteLine(x));
string fileNm1 = files.First();
string fileNm2 = files.Last();

var files = Directory.EnumerateDirectories(
 path: @"D:\a", 検索開始ディレクトリ
 searchPattern: "?0", 検索パターン
 searchOption: SearchOption.AllDirectories);

SearchOption.TopDirectoryOnly 指定ディレクトのみ
SearchOption.AllDirectories サブ・ディレクトも含める

その他

畳み込み(fold・Aggregate)

var ary = new[] { 1, 2, 3, 4, 5 };

var map = ary.Aggregate((pSum, elm) => pSum + elm);
右辺の結果を左辺の第一引数とする
第一引数の初期値は0? 第二引数は配列の値がLoopされる
pSum + elm → pSum

var map = ary.Aggregate((pMax, elm) => (pMax < elm) ? elm : pMax);
右辺の結果を左辺の第一引数とする
第一引数の初期値は0? 第二引数は配列の値がLoopされる
(pMax < elm) ? elm : pMax
if (pMax < elm) {
 pMax = elm
}else{
 pMax = pMax
}

var map = ary.Aggregate(10, (pCnt, elm) => pCnt += 1);
指定無し第一引数の初期値は0。 第二引数は配列の値がLoopされる
明示的に初期値を与える事も可能