ようなものは無かったので、StackOverflowの内容を参考に作ってみた。
■動機
・Distinctの引数に、OrderBy等と同様にラムダ式を引数とするようにして使いたかった。
・しかし、Distinctの引数はIEqualityComparer(Of TSource)でしか取れない。
・GroupByではラムダ式を引数に取れるので理想に近いけど、次の理由により構文が冗長になるためなんとも。。
・戻り値リストの要素が絞り込んだプロパティ値しか持たない匿名オブジェクト?
になる(SQLのGROUP BYのイメージ)
⇒後続処理にて、絞り込み要素とは別の要素でごにょごにょしたい場合不便。
・リスト要素のプロパティ値でしか絞り込めない
⇒リスト要素が辞書型の場合、事前に絞込み用のクラス型へ変換する必要がある。
■参考
StackOverflowの回答にてC#で紹介されているDistinctBy関数を
VB.NETでIEnumerableの拡張メソッドとして実装してみた。
■実装
Imports System.Runtime.CompilerServices
Module EnumerableExtention
'IEnumerable型の拡張メソッドを定義
<Extension()> _
Public Iterator Function DistinctBy(Of TSource, TKey)(ByVal source As IEnumerable(Of TSource), keySelector As Func(Of TSource, TKey)) As IEnumerable(Of TSource)
Dim knownKeys As New HashSet(Of TKey)
For Each element As TSource In source
If knownKeys.Add(keySelector(element)) Then
Yield element
End If
Next
End Function
End Module
■使い方
・Dictionaryの配列を単一化することが可能
<TestMethod()> _
Public Sub DistinctByTest_Dictinary()
Const KEY_CODE As String = "Code"
Const KEY_VALUE As String = "Value"
Dim list = New List(Of Dictionary(Of String, String)) From _
{ _
New Dictionary(Of String, String) From {{KEY_CODE, "A"}, {KEY_VALUE, "あああ"}}, _
New Dictionary(Of String, String) From {{KEY_CODE, "B"}, {KEY_VALUE, "びびび"}}, _
New Dictionary(Of String, String) From {{KEY_CODE, "A"}, {KEY_VALUE, "あああ"}}, _
New Dictionary(Of String, String) From {{KEY_CODE, "C"}, {KEY_VALUE, "ししし"}} _
}
Dim codeEqualA = Function(item As Dictionary(Of String, String))
Return item(KEY_CODE) = "A"
End Function
Assert.AreEqual(2, list.FindAll(codeEqualA).Count)
Dim result = list.DistinctBy(Function(item) item(KEY_CODE)).ToList
Assert.AreEqual(1, result.FindAll(codeEqualA).Count)
End Sub
・複数項目で単一化することも可能
Private Class CodeValue
Public Property Code As String
Public Property Code2 As String
Public Property Value As String
End Class
<TestMethod()> _
Public Sub DistinctByTest_Class()
Dim list = New List(Of CodeValue) From _
{ _
New CodeValue With {.Code = "A", .Code2 = "A", .Value = "あああ"}, _
New CodeValue With {.Code = "A", .Code2 = "B", .Value = "ああび"}, _
New CodeValue With {.Code = "B", .Code2 = "A", .Value = "びびあ"}, _
New CodeValue With {.Code = "B", .Code2 = "B", .Value = "びびび"}, _
New CodeValue With {.Code = "A", .Code2 = "A", .Value = "あああ"}, _
New CodeValue With {.Code = "C", .Code2 = "C", .Value = "ししし"} _
}
Dim codeEqualA = Function(item As CodeValue)
Return item.Code = "A"
End Function
Assert.AreEqual(3, list.FindAll(codeEqualA).Count)
'複数項目(Code + Code2 の組み合わせ)で絞り込むことも可能
Dim result = list.DistinctBy(Function(item) New With {Key item.Code, _
Key item.Code2}).ToList
Assert.AreEqual(2, result.FindAll(codeEqualA).Count)
End Sub