私はDataTable
を持っています(現在は複数の列を持っていますが、それが簡単になれば、1つの列を取得することもできます)。 String
の値がDataTable
の列に存在するかどうかを確認したいと思います。 (私は何度もやっているので、適度に速くしたいです。)
これを行う良い方法は何ですか?毎回DataTable
行を反復処理することは、悪い方法のようです。列をフラット_List/Array
_形式に変換し、組み込み関数を使用できますか? myStrList.Contains("value")
のようなもの?
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
にある行の数、および見つかったアイテムをどのように処理する必要があるかによって異なります。最善の方法は、テストケースで1つずつ試して、必要なものに最適なものを確認することです。
select
を使用して、その値が存在するかどうかを確認できます。もしそうなら、それは行を返すか、そうしません。ここにあなたを助けるためのいくつかのサンプルコードがあります。
Dim foundRow() As DataRow
foundRow = dt.Select("SalesCategory='HP'")
Select( selectはインデックスを使用しない )の代わりに row filter または 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
の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;