インデックス付きSelectメソッド
コレクションの要素を処理する際、ナンバリングが必要なことがたまにあります。
通常であれば、以下のようにfor文を使ってナンバリングするのでしょうが・・・。
string[] values = { "京都", "奈良", "大阪", "兵庫", "滋賀", "和歌山" }; var res = new List<string>(); for (int i= 0; i < values.Count(); ++i) { res.Add(i.ToString() + ":" + values[i]); } Console.WriteLine(String.Join(",", res));
Dim values As String() = {"京都", "奈良", "大阪", "兵庫", "滋賀", "和歌山"} Dim res = New List(Of String)() For i As Integer = 0 To values.Count() - 1 res.Add(i.ToString() & ":" & values(i)) Next Console.WriteLine(String.Join(",", res))
0:京都,1:奈良,2:大阪,3:兵庫,4:滋賀,5:和歌山
LINQのSelectメソッドはインデックスを射影するオーバーロードがあります。
var res = values.Select((x, index) => index.ToString() + ":" + x).ToArray(); Console.WriteLine(String.Join(",", res));
Dim res = values.Select(Function(x, index) index.ToString() & ":" & x).ToArray() Console.WriteLine(String.Join(",", res))
インデックスを利用して、Whereメソッドに応用することも可能です。
また、インデックスはユニークですので、これを利用してDictionaryクラスに変換することも可能です。
var dic = values1.Select((x, index) => new { index, x }).ToDictionary(x => x.index, x => x.x);
Dim dic = values1.Select(Function(x, index) New With {index, x}).ToDictionary(Function(x) x.index, Function(x) x.x)
NULLを考える①(NULLIF関数)
データベースを扱う上で必ずと言っていいほどお世話になる「NULL」さん。何もないという意味ですが、C#などのプログラミング言語に比べると、SQLのNULLは少し「できる奴」だと個人的には思います。
ということで、NULLを扱う関数を全てではありませんが少し考えてまとめてみました。
今回はNULLIF関数です。
NULLIF関数は第一引数と第二引数が一致する場合NULLを返し、違う場合は一つ目の引数を返します。つまり、0かどうかを判断するようにすれば良いということになります。
--フィールド名の値が0の場合、NULLが返る NULLIF( [フィールド名] , 0) --フィールド名の値が100の場合、NULLが返る NULLIF( [フィールド名] , 100)
これはCASE文と動作的には全く一緒となります。
--フィールド名の値が0の場合、NULLが返る CASE WHEN [フィールド名] = 0 THEN NULL ELSE 0 END --フィールド名の値が100の場合、NULLが返る CASE WHEN [フィールド名] = 100 THEN NULL ELSE 0 END
NULLIF関数の方がスマートですよね。
集合メソッド(Union、Except、Intersect)
複数のコレクションに対して集合を提供します。
Unionメソッド(和集合)
2つのコレクションの和集合を返します。
和集合とは、集合A、集合Bが定義されている場合、A又はBどちらか一方の集合に属する集合全体「A∪B」のことを指します。
string[] values1 = { "京都", "奈良", "大阪", "兵庫", "滋賀" ,"和歌山"}; string[] values2 = { "東京", "大阪", "愛知", "京都", "福岡", "兵庫" }; var res = values1.Union(values2);
Dim values1 As String() = {"京都", "奈良", "大阪", "兵庫", "滋賀", "和歌山"} Dim values2 As String() = {"東京", "大阪", "愛知", "京都", "福岡", "兵庫"} Dim res = values1.Union(values2)
京都,奈良,大阪,兵庫,滋賀,和歌山,東京,愛知,福岡
Exceptメソッド(差集合)
2つのコレクションの差集合を返します。
差集合とは、集合A、集合Bが定義されている場合、集合Aから集合Bを取り除いた集合「A\B」のことです。
string[] values1 = { "京都", "奈良", "大阪", "兵庫", "滋賀" ,"和歌山"}; string[] values2 = { "東京", "大阪", "愛知", "京都", "福岡", "兵庫" }; var res = values1.Except(values2);
Dim values1 As String() = {"京都", "奈良", "大阪", "兵庫", "滋賀", "和歌山"} Dim values2 As String() = {"東京", "大阪", "愛知", "京都", "福岡", "兵庫"} Dim res = values1.Except(values2)
奈良,滋賀,和歌山
Intersectメソッド(積集合)
2つのコレクションの積集合を返します。
積集合は共通集合、交叉とも呼ばれ、集合A、集合Bが定義されている場合、互いに含まれる部分の集合「A∩B」のことを指します。
string[] values1 = { "京都", "奈良", "大阪", "兵庫", "滋賀" ,"和歌山"}; string[] values2 = { "東京", "大阪", "愛知", "京都", "福岡", "兵庫" }; var res = values1.Intersect(values2);
Dim values1 As String() = {"京都", "奈良", "大阪", "兵庫", "滋賀", "和歌山"} Dim values2 As String() = {"東京", "大阪", "愛知", "京都", "福岡", "兵庫"} Dim res = values1.Intersect(values2)
京都,大阪,兵庫
取得メソッド②(Skip、Take、SkipWhile、TakeWhile)
コレクションの特定要素を取得する際に利用します。Selectメソッドの場合だとコレクションを上から順番に評価していきますが、予め取得したい要素の場所が決まっているような場合は走査が無駄になる場合があります。その場合はこれらのメソッドを活用します。
なお、コレクションは以下を共通で利用します。
int[] values = { 1, 9, 5, 6, 8, 6, 2, 5, 3 };
Dim values As Integer() = {1, 9, 5, 6, 8, 6, 2, 5, 3}
Skipメソッド
コレクションの先頭から指定された要素数を除外して、以降をコレクションとして返します。
例えば、3番目までスキップして4番目以降をコレクションとして返す場合は以下の通りです。
Console.WriteLine(string.Join(",",values.Skip(3)));
Console.WriteLine(String.Join(",", values.Skip(3)))
6,8,6,2,5,3
Takeメソッド
Skipメソッドと逆で、コレクションの先頭から指定された要素数を返します。
例えば、3番目までのコレクションを取得する場合は以下の通りです。
Console.WriteLine(string.Join(",", values.Take(3)));
Console.WriteLine(String.Join(",", values.Take(3)))
1,9,5
SkipWhileメソッド
Skipメソッドの条件あり版で、コレクションの先頭から走査して指定された条件が「偽」の間は除外し、「真」以降の要素をコレクションとして返します。
例えば、要素が8と一致するところまで除外する場合は以下の通りです。
Console.WriteLine(string.Join(",", values.SkipWhile(x => x != 8)));
Console.WriteLine(String.Join(",", values.SkipWhile(Function(x) x <> 8)))
8,6,2,5,3
TakeWhileメソッド
Takeメソッドの条件あり版で、コレクションの先頭から走査して指定された条件が「偽」になるまでの要素をコレクションとして返します。
例えば、要素が8と一致するまでのコレクションを取得する場合は以下の通りです。
Console.WriteLine(string.Join(",", values.TakeWhile(x => x != 8)));
Console.WriteLine(String.Join(",", values.TakeWhile(Function(x) x <> 8)))
1,9,5,6
ガード節によるネスト防止
ステートメントをネストし過ぎると可読性が著しく低下します。逆に言えばネストが少なければ見やすくなるのです。
課題
以下のような数値をチェックするような関数を考えます。
private int test1(int x, int y, int z) { int res = 0; if (x < 0) { res = 1; } else { if (y < 0) { res = 2; } else { if (z < 0) { res = 3; } } } return res; }
Private Function test1(ByVal x As Integer, ByVal y As Integer, ByVal z As Integer) As Integer Dim res As Integer = 0 If x < 0 Then res = 1 Else If y < 0 Then res = 2 Else If z < 0 Then res = 3 End If End If End If Return res End Function
リファクタリング
この場合はガード節を利用したリファクタリングが効果的です。ガード節とは、レアケースと通常ケースを切り分け、早めにreturnなどで抜けてしまう手法をいいます。ガード節を利用するとステートメントのネストを減らすことができ、また異常系と正常系の切り分けが認識しやすくなるメリットがあります。
private int test2(int x,int y,int z) { if (x < 0) return 1; if (y < 0) return 2; if (z < 0) return 3; return 0; }
Private Function test2(ByVal x As Integer, ByVal y As Integer, ByVal z As Integer) As Integer If (x < 0) Then Return 1 If (y < 0) Then Return 2 If (z < 0) Then Return 3 Return 0 End Function
このようにガード節を用いると、「条件に全く一致しない場合は、初期値を返す(何もしない)」を明確に提供することが出来ます。
集計メソッド(Max、MIn、Average、Sum、Count)
数値コレクションの最大値、最小値、平均値、合計、コレクション数を取得することができるメソッドです。
int[] values = { 1, 9, 5, 6, 8, 6, 2, 5, 3 }; Console.WriteLine(values.Max()); Console.WriteLine(values.Min()); Console.WriteLine(values.Average()); Console.WriteLine(values.Sum()); Console.WriteLine(values.Count());
Dim values As Integer() = {1, 9, 5, 6, 8, 6, 2, 5, 3} Console.WriteLine(values.Max()) Console.WriteLine(values.Min()) Console.WriteLine(values.Average()) Console.WriteLine(values.Sum()) Console.WriteLine(values.Count())
9 1 5 45 9
取得メソッド①(First、FirstOrDefaullt、Last、LastOrDefault)
Firstメソッドはコレクションの先頭を取得するメソッドです。
int[] values = { 1, 9, 5, 6, 8, 6, 2, 5, 3 }; Console.WriteLine(values.First());
Dim values As Integer() = {1, 9, 5, 6, 8, 6, 2, 5, 3} Console.WriteLine(values.First())
1
正直単体で使用することはほとんどないメソッドですが。
Firstメソッドは他にも引数に条件を指定することで、最初に条件に合った要素を取得することが可能です。
//偶数の最初のデータを取得 Console.WriteLine(values.First(x => x % 2 == 0));
'偶数の最初のデータを取得 Console.WriteLine(values.First(Function(x) x Mod 2 = 0))
6
Firstメソッドの欠点として、コレクションがnullの場合や条件に合った要素が無い場合、System.InvalidOperationExceptionの例外が発生してしまいます。これを回避するメソッドとして、FirstOrDefaultメソッドがあります。FirstOrDefaultメソッドはSystem.InvalidOperationException例外が発生する場合、対象の型の初期値を返します。
Console.WriteLine(values.FirstOrDefault(x => x > 9));
Console.WriteLine(values.FirstOrDefault(Function(x) x > 9))
0
但し、この場合0が最初に見つかったので0を返したのか、例外が発生する状況になったので0を返したのかの判断ができませんので、実際のコーディングではもう少し工夫が必要です。
First、FirstOrDefaultメソッドの逆で、最後の要素から検索するメソッドがLast、LastOrDefaultメソッドです。こちらも例外発生と初期値を返す挙動はFirst、FirstOrDefaultメソッドと同じです。