LINQ (in VB.NET) をショートコードで学んでいきます



## はじめに

[LINQをショートコードで学んでいく - Qiita](https://qiita.com/esakun/items/2acef5085dc766ca5552)

[初心者LINQerがLINQと戯れる : UniRx強化月間 - 渋谷ほととぎす通信](http://www.shibuya24.info/entry/2015/07/18/125824)

上記リンクの記事をリスペクトしつつ、コードを VB.NET に移植しながら LINQ を学んでいきます。

## 各コードで操作する配列

```vb:配列の初期化
Dim nums() As Integer = New Integer() {1, -2, 3, 4, 5, 6, 7}
```

## （例１）3 よりも大きい値の配列を結果として取得する

整数の配列 `1, -2, 3, 4, 5, 6, 7` から `3` より大きい値を LINQ で抜き出します。`4, 5, 6, 7` が期待される結果です。

リスペクト元記事同様に、最初にクエリ構文で書いてみます。

### クエリ構文

```vb:3よりも大きい値の配列を結果として取得する（クエリ構文）
' 操作の対象の配列を初期化する
Dim nums() As Integer = New Integer() {1, -2, 3, 4, 5, 6, 7}

' LINQ で 3 よりも大きい値の配列を結果として取得する
Dim linqResult As IEnumerable(Of Integer)
linqResult = From n As Integer In nums
             Where n > 3
             Select n

' 結果を表示するためのコード
Dim result As New StringBuilder
Dim comma As String = ""
With result
    For Each n As Integer In linqResult
        .Append(comma & n)
        comma = ", "
    Next
End With
System.Diagnostics.Debug.Print(result.ToString())
```

結果を表示するためのコードが鬼のように長い。何とかできないでしょうか。

```vb:3よりも大きい値の配列を結果として取得する（クエリ構文：その２）
' 操作の対象の配列を初期化する
Dim nums() As Integer = New Integer() {1, -2, 3, 4, 5, 6, 7}

' LINQ で 3 よりも大きい値の配列を結果として取得する
Dim linqResult As IEnumerable(Of String)
linqResult = From n As Integer In nums
             Where n > 3
             Select CStr(n)

' 結果を表示するためのコード
System.Diagnostics.Debug.Print(Join(linqResult.ToArray(), ", "))
```

なんとかはなりましたが、結果表示のためだけに linqResult の型まで変えてしまうのはサンプルとしては失格ではないでしょうか。以下のサンプルコードは結果を表示するためのコードは省略することにしますね。

また、操作したい元配列は全て同じなので、配列の初期化コードも省略することにします。

### メソッド構文

次にメソッドチェーンと無名関数を組み合わせたメソッド構文で書いてみます。

```vb:3よりも大きい値の配列を結果として取得する（メソッド構文）
Dim linqResult As IEnumerable(Of Integer)
linqResult = nums _
    .Where(Function(n) n > 3) _
    .Select(Function(n) n)

```

LINQ を調べると、C# のサンプルコードが多く見受けられます。それらを見ると C# ではメソッド構文が好まれている印象があります。しかしながら VB.NET でメソッド構文を用いるとやや冗長な感じがして、クエリ構文の方がすっきりしています。

自分としては SQL に似たクエリ構文の方が、圧倒的になじみのあり、また VB.NET 上ですっきり書けるので好ましいです。逆に SQL になじみがなかったり C# を使っている方はメソッド構文を好むのでしょうね。C# のサンプル構文にメソッド構文が多い理由が解る気がします。

## （例２）3 より大きく、且つ、2 で割り切れる値の配列を取得する

結果として `4, 6` を期待するコードです。

### クエリ構文

```vb:3より大きい且つ、2で割り切れる値の配列を取得する（クエリ構文）
Dim linqResult As IEnumerable(Of Integer)
linqResult = From n As Integer In nums
             Where 1 = 1 _
                And n > 3 _
                And n Mod 2 = 0
             Select n

```

Where 句に条件を増やすことになりますが、私は SQL では最初に `1=1` して `AND` を揃えて書くのが好きです。上記サンプルもそのように書いてみました。

しかし LINQ では Where 句を二行に分けることも出来ます。

```vb:3より大きい且つ、2で割り切れる値の配列を取得する（クエリ構文：その２）
Dim linqResult As IEnumerable(Of Integer)
linqResult = From n As Integer In nums
             Where n > 3
             Where n Mod 2 = 0
             Select n
```

SQL から考えるとへんな気がしますが、行の継続記号を書かなくて済むし、`1=1` しなくても条件のインデントが揃うので、こちらの方が好きです。

### メソッド構文

メソッド構文も二通りのサンプルを示します。

```vb:3より大きい且つ、2で割り切れる値の配列を取得する（メソッド構文）
Dim linqResult As IEnumerable(Of Integer)
linqResult = nums _
    .Where(Function(n) n > 3 And n Mod 2 = 0) _
    .Select(Function(n) n)
```

```vb:3より大きい且つ、2で割り切れる値の配列を取得する（メソッド構文：その２）
Dim linqResult As IEnumerable(Of Integer)
linqResult = nums _
    .Where(Function(n) n > 3) _
    .Where(Function(n) n Mod 2 = 0) _
    .Select(Function(n) n)
```

## （例３）3より大きい配列の各要素に文字列 "piyo" を付与した配列を取得する

`4piyo, 5piyo, 6piyo, 7piyo` のような結果を期待します。

返値が整数ではなく文字列になるので結果出力のためのコードも含めます。

### クエリ構文

```vb:3より大きい配列の各要素に文字列"piyo"を付与した配列を取得する（クエリ構文）
Dim linqResult As IEnumerable(Of String)
linqResult = From n As Integer In nums
             Where n > 3
             Select n & "piyo"

System.Diagnostics.Debug.Print(Join(linqResult.ToArray(), ", "))
```

### メソッド構文

```vb:3より大きい配列の各要素に文字列"piyo"を付与した配列を取得する（メソッド構文）
linqResult = nums _
    .Where(Function(n) n > 3) _
    .Select(Function(n) n & "piyo")

System.Diagnostics.Debug.Print(Join(linqResult.ToArray(), ", "))
```

## （例４）異なる型が混在する配列から String 型だけを抽出する

### クエリ構文

```vb:異なる型が混在する配列からString型だけを抽出する（クエリ構文）
Dim mix() As Object = New Object() {"hoge", "piyo", 123, "foo", 456}
Dim linqResult As IEnumerable(Of Object)
linqResult = From el As Object In mix.OfType(Of String)
             Select el

System.Diagnostics.Debug.Print(Join(linqResult.ToArray(), ", "))
```

### メソッド構文

```vb:異なる型が混在する配列からString型だけを抽出する（メソッド構文）
Dim mix() As Object = New Object() {"hoge", "piyo", 123, "foo", 456}
Dim linqResult As IEnumerable(Of Object)
linqResult = mix.OfType(Of String) _
    .Select(Function(el) el)

System.Diagnostics.Debug.Print(Join(linqResult.ToArray(), ", "))
```

## （例５）匿名型で受け取る

リスペクト記事ではこれがどのような結果を期待するのかわからないため、はずしている可能性が大きいですが。

### クエリ構文

```vb:匿名型で受け取る（クエリ構文）
Dim nums() As Integer = New Integer() {1, -2, 3, 4, 5, 6, 7}
Dim linqResult = From n As Object In nums
                 Where n > 5
                 Select CObj(n)

' 結果を表示するためのコード
Dim result As New StringBuilder
Dim comma As String = ""
With result
    For Each n As String In linqResult
        .Append(comma & n)
        comma = ", "
    Next
End With
System.Diagnostics.Debug.Print(result.ToString())
```

### メソッド構文

```vb:匿名型で受け取る（メソッド構文）
Dim nums() As Integer = New Integer() {1, -2, 3, 4, 5, 6, 7}
Dim linqResult = nums _
    .Where(Function(n) n > 5) _
    .Select(Function(n) CObj(n))

' 結果を表示するためのコード
Dim result As New StringBuilder
Dim comma As String = ""
With result
    For Each n As String In linqResult
        .Append(comma & n)
        comma = ", "
    Next
End With
System.Diagnostics.Debug.Print(result.ToString())
```

## （例２.２）2番目以降の要素で且つ、3より大きく且つ、2で割り切れる値の配列を取得する

### メソッド構文

こればかりはクエリ構文での書き方が解りませんでした。

```vb:2番目以降の要素で且つ、3より大きく且つ、2で割り切れる値の配列を取得する（メソッド構文）
Dim linqResult = nums _
    .Where(Function(n, index) index > 2 And n > 3 And n Mod 2 = 0) _
    .Select(Function(n) n)
```

## おわりに

例３以外は `Select` を省略しても良いようです。

VB.NET では行継続記号を書かなくて済むクエリ構文の方がすっきりして書きやすい気がします。