はじめての LINQ (VB.NET)

この記事は VB.NET で開発をしているのに、まだ LINQ（リンク）が使えていない私が
こちらの記事で LINQ を勉強したとき作成したソースコードになります。

はじめての LINQ
https://qiita.com/nskydiving/items/c9c47c1e48ea365f8995

※ソースコードのみとなりますのでオリジナルの記事とあわせてご覧ください。

# **LINQ をはじめる前に**

### **・クエリ構文とメソッド構文**

```vb:
Dim list = New List(Of Integer)(New Integer() {1, 84, 95, 95, 40, 6})

'' クエリ構文
Dim query = From x In list
            Where x Mod 2 = 0
            Order By x
            Select x * 3

'' メソッド構文（この記事ではこちらを採用）
Dim query = list _
            .Where(Function(x) x Mod 2 = 0) _
            .OrderBy(Function(x) x) _
            .Select(Function(x) x * 3)
```

### **・型推論**

```vb:
Dim list = New List(Of Integer)(New Integer() {1, 84, 95, 95, 40, 6})
'Dim list As List(Of Integer) = New List(Of Integer)(New Integer() {1, 84, 95, 95, 40, 6})  '' VB.NET 9.0 以前の書き方

For Each x In list
    Console.WriteLine(x)
Next
```

### **・匿名クラス**

```vb:
Dim fruitList = {
    New With {.Name = "りんご", .Price = 300},
    New With {.Name = "バナナ", .Price = 200},
    New With {.Name = "パイナップル", .Price = 1000},
    New With {.Name = "いちご", .Price = 500}
}

For Each fruit In fruitList
    Console.WriteLine(fruit)
Next
```

### **・ラムダ式**

```vb:
Dim list = New List(Of Integer)(New Integer() {1, 84, 95, 95, 40, 6})

'' list の要素から偶数を取り出す
Dim findAllList = list.FindAll(Function(x) x Mod 2 = 0)
Console.WriteLine("=== findAllList ===")
For Each x In findAllList
    Console.WriteLine(x)
Next

'' list の要素をそれぞれ 3 倍にする
Dim convertAllList = list.ConvertAll(Function(x) x * 3)
Console.WriteLine("=== convertAllList ===")
For Each x In convertAllList
    Console.WriteLine(x)
Next

'' VB.NETは匿名メソッドがないので省略
```

# **LINQ の使い方**

### **・要素の取得**

```vb:
Dim list = New List(Of Integer)(New Integer() {1, 84, 95, 95, 40, 6})

'' list の最初の要素を取得する
Console.WriteLine("First: " + list.First().ToString)

'' list の最後の要素を取得する
Console.WriteLine("Last: " + list.Last().ToString)
```

### **・集計**

```vb:
Dim list = New List(Of Integer)(New Integer() {1, 84, 95, 95, 40, 6})

'' list の最大値を取得
Console.WriteLine("Max: " + list.Max().ToString)

'' list の最小値を取得
Console.WriteLine("Min: " + list.Min().ToString)

'' list の平均値を取得
Console.WriteLine("Average: " + list.Average().ToString)

'' list の合計値を取得
Console.WriteLine("Sum: " + list.Sum().ToString)

'' list の要素値を取得
Console.WriteLine("Count: " + list.Count().ToString)
```

### **・変換**

```vb:
Dim list = New List(Of Integer)(New Integer() {1, 84, 95, 95, 40, 6})

'' list を配列に変換
Dim array As Integer() = list.ToArray()
Console.WriteLine("=== array ===")
For Each x In array
    Console.WriteLine(x)
Next

'' list を object 型の List に変換
Dim objectList As List(Of Object) = list.Cast(Of Object)().ToList()
Console.WriteLine("=== objectList ===")
For Each x In objectList
    Console.WriteLine(x)
Next
```

### **・複数要素の取得**

```vb:
Dim list = New List(Of Integer)(New Integer() {1, 84, 95, 95, 40, 6})

'' list から重複を除いた要素を取得する
Dim distinctList = list.Distinct()
Console.WriteLine("=== distinctList ===")
For Each x In distinctList
    Console.WriteLine(x)
Next

'' list の先頭から指定された数の要素をスキップして残りの要素を取得する
Dim skipList = list.Skip(3)
Console.WriteLine("=== skipList ===")
For Each x In skipList
    Console.WriteLine(x)
Next

'' list の先頭から指定された数の要素を取得する
Dim takeList = list.Take(3)
Console.WriteLine("=== takeList ===")
For Each x In takeList
    Console.WriteLine(x)
Next
```

### **・判定**

```vb:
Dim list = New List(Of Integer)(New Integer() {1, 84, 95, 95, 40, 6})

'' list のすべての要素が 100 未満かどうか
Console.WriteLine("All: " + list.All(Function(x) x < 100).ToString)

'' list のいずれかの要素が 0 未満かどうか
Console.WriteLine("Any: " + list.Any(Function(x) x < 0).ToString)

'' list に値が 40 の要素が含まれてるかどうか
Console.WriteLine("Contains: " + list.Contains(40).ToString)
```

### **・集合**

```vb:
Dim list1 = New List(Of Integer)(New Integer() {1, 84, 95, 95, 40, 6})
Dim list2 = New List(Of Integer)(New Integer() {1, 16, 39, 33, 7, 84})

'' 和集合を取得する
Dim unionList = list1.Union(list2)
Console.WriteLine("=== unionList ===")
For Each x In unionList
    Console.WriteLine(x)
Next

'' 差集合を取得する
Dim exceptList = list1.Except(list2)
Console.WriteLine("=== exceptList ===")
For Each x In exceptList
    Console.WriteLine(x)
Next

'' 積集合を取得する
Dim intersectList = list1.Intersect(list2)
Console.WriteLine("=== intersectList ===")
For Each x In intersectList
    Console.WriteLine(x)
Next
```

### **・ソート**

```vb:
Dim fruitList = {
    New With {.Name = "りんご", .Price = 300},
    New With {.Name = "バナナ", .Price = 200},
    New With {.Name = "パイナップル", .Price = 1000},
    New With {.Name = "いちご", .Price = 500}
}

'' fruitList を Price が昇順になるように並び替える
Dim orderByList = fruitList.OrderBy(Function(x) x.Price)
Console.WriteLine("=== orderByList ===")
For Each x In orderByList
    Console.WriteLine(x)
Next

'' fruitList を Price が降順になるように並び替える
Dim orderByDescendingList = fruitList.OrderByDescending(Function(x) x.Price)
Console.WriteLine("=== orderByDescendingList ===")
For Each x In orderByDescendingList
    Console.WriteLine(x)
Next

'' fruitList を逆順に並び替える
Dim reverseList = fruitList.Reverse()
Console.WriteLine("=== reverseList ===")
For Each x In reverseList
    Console.WriteLine(x)
Next
```

### **・LINQ メソッドの組み合わせ**

```vb:
Dim list = New List(Of Integer)(New Integer() {1, 84, 95, 95, 40, 6})

'' LINQ メソッドを組み合わせて使う
'' 重複要素を削除、先頭から指定された数の要素をスキップ、昇順に並び替え
Dim resultList1 = list _
                  .Distinct() _
                  .Skip(2) _
                  .OrderBy(Function(x) x)
Console.WriteLine("=== resultList1 ===")
For Each x In resultList1
    Console.WriteLine(x)
Next

'' LINQ 以外のメソッド（FindAll、ConvertAll）も組み合わせて使う
'' ConvertAll メソッドを使用するため一旦 List に変換する
Dim resultList2 = list _
                  .FindAll(Function(x) x Mod 2 = 0) _
                  .OrderBy(Function(x) x) _
                  .ToList() _
                  .ConvertAll(Function(x) x * 3)
Console.WriteLine("=== resultList2 ===")
For Each x In resultList2
    Console.WriteLine(x)
Next
```

# **Select と Where**

```vb:
Dim list = New List(Of Integer)(New Integer() {1, 84, 95, 95, 40, 6})

'' LINQ 以外のメソッド（FindAll、ConvertAll）も組み合わせて使う
'' ConvertAll メソッドを使用するため一旦 List に変換する
Dim resultList1 = list _
                  .FindAll(Function(x) x Mod 2 = 0) _
                  .OrderBy(Function(x) x) _
                  .ToList() _
                  .ConvertAll(Function(x) x * 3)
Console.WriteLine("=== resultList1 ===")
For Each x In resultList1
    Console.WriteLine(x)
Next

'' FindAll メソッドを Where メソッドに、ConvertAll メソッドを Select メソッドに書き換え（処理は同じ）
Dim resultList2 = list _
                  .Where(Function(x) x Mod 2 = 0) _
                  .OrderBy(Function(x) x) _
                  .Select(Function(x) x * 3)
Console.WriteLine("=== resultList2 ===")
For Each x In resultList2
    Console.WriteLine(x)
Next

'' クエリ構文で書き換え（処理は同じ）
'' （実は最初に紹介した「LINQ の典型的な例」と同じ）
Dim resultList3 =
    From x In list
    Where x Mod 2 = 0
    Order By x
    Select x * 3
Console.WriteLine("=== resultList3 ===")
For Each x In resultList3
    Console.WriteLine(x)
Next
```

# **LINQ のメリット**

### **・少ない行数、ネスト数で簡潔なコードが書ける**

```vb:
Dim list = New List(Of Integer)(New Integer() {1, 84, 95, 95, 40, 6})

'' LINQ を使って実装
'' ========================================
Dim resultList1 = list _
                  .Where(Function(x) x Mod 2 = 0) _
                  .OrderBy(Function(x) x) _
                  .Select(Function(x) x * 3)
Console.WriteLine("=== resultList1 ===")
For Each x In resultList1
    Console.WriteLine(x)
Next
'' ========================================

'' LINQ は使わず、For Each と For を使って実装
'' ========================================
Dim resultList2 = New List(Of Integer)
For Each x In list
    If x Mod 2 = 0 Then
        resultList2.Add(x)
    End If
Next

resultList2.Sort(Function(x, y) x.CompareTo(y))

For i As Integer = 0 To resultList2.Count() - 1
    resultList2(i) *= 3
Next

Console.WriteLine("=== resultList2 ===")
For Each x In resultList2
    Console.WriteLine(x)
Next
'' ========================================
```

# **感想**

食わず嫌いしていましたが、
オリジナルの記事を参考に勉強したら驚くほどすんなり覚えることができました。感謝です！

クエリ構文とメソッド構文はどっちがいいのかな？とか考えていましたが
記事はほとんどメソッド構文で書かれていますが、終わってみたらクエリ構文も書けるようになっていました。