私はDataTable
(現在複数の列を持っていますが、簡単にすれば1つの列だけをつかむことができます)があります。String
の列に値が存在するかどうかを確認したいDataTable
。(何度もやっているので、かなり速くしたいです。)
これを行う良い方法は何ですか?DataTable
毎回行を反復するのは悪い方法のようです。列をフラットList/Array
形式に変換し、組み込み関数を使用できますか? みたいなmyStrList.Contains("value")
?
select
その値が存在するかどうかを調べるために使用できます。その場合、行が返されるか返されません。ここに役立つサンプルコードがあります。
Dim foundRow() As DataRow
foundRow = dt.Select("SalesCategory='HP'")
データがDataTable
あまり頻繁に変更されず、DataTable
複数回検索し、DataTable
多くの行が含まれている場合は、データの独自のインデックスを作成する方がはるかに高速になる可能性があります。
これを行う最も簡単な方法は、キー列でデータを並べ替えて、並べ替えられたリストでバイナリ検索を実行できるようにすることです。たとえば、次のようにインデックスを作成できます。
Private Function BuildIndex(table As DataTable, keyColumnIndex As Integer) As List(Of String)
Dim index As New List(Of String)(table.Rows.Count)
For Each row As DataRow in table.Rows
index.Add(row(keyColumnIndex))
Next
index.Sort()
Return index
End Function
次に、次のようにバイナリ検索を使用して、値がインデックスに存在するかどうかをすばやく確認できます。
Private Function ItemExists(index As List(Of String), key As String) As Boolean
Dim index As Integer = index.BinarySearch(key)
If index >= 0 Then
Return True
Else
Return False
End If
End Function
単純な文字列配列でも同じことができます。または、Dictionary
オブジェクト (ハッシュ テーブルの実装) を使用して、次のように のハッシュ インデックスを作成することもできDataTable
ます。
Private Function BuildIndex(table As DataTable, keyColumnIndex As Integer) As Dictionary(Of String, DataRow)
Dim index As New Dictionary(Of String, DataRow)(table.Rows.Count)
For Each row As DataRow in table.Rows
index(row(keyColumnIndex)) = row
Next
Return index
End Function
DataRow
次に、次のように、特定のキーの一致を取得できます。
Dim index As Dictionary(Of String, DataRow) = BuildIndex(myDataTable, myKeyColumnIndex)
Dim row As DataRow = Nothing
If index.TryGetValue(myKey, row) Then
' row was found, can now use row variable to access all the data in that row
Else
' row with that key does not exist
End If
SortedList
またはSortedDictionary
クラスの使用を検討することもできます。これらは両方ともバイナリ ツリーの実装です。これらすべてのオプションのどれが特定のシナリオで最速になるかを言うのは困難です. それはすべて、データのタイプ、インデックスを再構築する必要がある頻度、検索する頻度、にある行の数DataTable
、および見つかったアイテムをどうする必要があるかによって異なります。最善の方法は、テスト ケースでそれぞれを試して、必要なものに最も適したものを確認することです。
選択の代わりに行フィルターまたはDataTable.Rows.Find()を使用する必要があります (選択はインデックスを使用しません)。テーブル構造によっては、特に問題のフィールドが (ローカルで) インデックス付けされている場合、いずれの方法のパフォーマンスも、すべての行をループするよりもはるかに高速になるはずです。.NET では、一連のフィールドがインデックス化されるようにPrimaryKeyである必要があります。
フィールドがインデックス化されていない場合、選択と行フィルターの両方を避けます。クラスの複雑さのオーバーヘッドは別として、条件の正確さをコンパイル時にチェックしないためです。長いものであれば、たまにデバッグに多くの時間を費やすことになるかもしれません。
小切手を厳密にタイプすることが常に望ましいです。最初に基になる型を定義した後、このヘルパー メソッドを定義することもできます。これは、DataTable
後でクラスの拡張メソッドに変換できます。
Shared Function CheckValue(myTable As DataTable, columnName As String, searchValue As String) As Boolean
For row As DataRow In myTable.Rows
If row(columnName) = searchValue Then Return True
Next
Return False
End Function
またはそれのより一般的なバージョン:
Shared Function CheckValue(myTable As DataTable, checkFunc As Func(Of DataRow, Boolean)) As Boolean
For Each row As DataRow In myTable.Rows
If checkFunc(row) Then Return True
Next
Return False
End Function
とその使用法:
CheckValue(myTable, Function(x) x("myColumn") = "123")
行クラスMyColumn
に type のプロパティがある場合、次のString
ようになります。
CheckValue(myTable, Function(x) x.myColumn = "123")
上記のアプローチの利点の 1 つは、計算されたフィールドをチェック条件にフィードできることです。これは、ここではテーブル/データベース内のmyColumn
物理的なものと一致する必要がないためです。myColumn
bool exists = dt.AsEnumerable().Where(c => c.Field<string>("Author").Equals("your lookup value")).Count() > 0;