Поиск и замена внутри столбца строки в DataTable выполняется медленно?

#vb.net #search #replace #datatable

Вопрос:

Я извлекаю отдельные слова в строковом столбце таблицы данных (.dt), а затем заменяю уникальные значения другим значением, таким образом, по сути, меняя слова на другие слова. Оба подхода, перечисленные ниже, работают, однако для 90 тыс. записей процесс не очень быстрый. Есть ли способ ускорить любой из этих подходов?

Первый подход заключается в следующем:

    'fldNo is column number in dt
   For Each Word As String In DistinctWordList
      Dim myRow() As DataRow
      myRow = dt.Select(MyColumnName amp; "='" amp; Word amp; "'")
      For Each row In myRow
         row(fldNo) = dicNewWords(Word)
      Next
   Next
 

Второй подход, основанный на LINQ, заключается в следующем, и на самом деле он тоже не очень быстрый:

    Dim flds as new List(of String)
   flds.Add(myColumnName)
   For Each Word As String In DistinctWordsList
     Dim rowData() As DataRow = dt.AsEnumerable().Where(Function(f) flds.Where(Function(el) f(el) IsNot DBNull.Value AndAlso f(el).ToString = Word).Count = flds.Count).ToArray
     ReDim foundrecs(rowData.Count)
     Cnt = 0
     For Each row As DataRow In rowData
       Dim Index As Integer = dt.Rows.IndexOf(row)
       foundrecs(Cnt) = Index   1 'row.RowId
       Cnt  = 1
     Next
     For i = 0 To Cnt
       dt(foundrecs(i))(fldNo) = dicNewWords(Word)
     Next 
   Next
 

Комментарии:

1. Определите «не очень быстро»? Я не удивлен, что LINQ «на самом деле не очень быстрый» — LINQ больше ориентирован на краткость, а не на эффективность

2. Кроме того, можете ли вы более просто определить свою проблему? Вашу проблему трудно понять. это звучит так: «У меня есть список из 90 тысяч слов, некоторые из которых встречаются более одного раза, а некоторые-только один раз. Для тех, которые встречаются только один раз, я хочу поменять их местами на другое слово, которое у меня уже есть в поиске по словарю. 90 тысяч слов находятся в столбце 90 тысяч строк, доступных для данных, по одному слову в строке » — это правильно?

3. @Caius Jard — «в dt 90 тысяч записей», а не 90 тысяч слов. Кроме того, список отдельных слов, уже найденных в этом столбце, задается списком DistintWordsList, который был построен с использованием LINQ .Distinct(). Ваше беспокойство по поводу «некоторые слова могут встречаться один или несколько раз» не имеет значения, поскольку уникальные слова являются уникальными словами — если слово встречается один или несколько раз, оно все равно уникально. Нет — ни одного слова в строке, повторяется много слов — отсюда и термин «уникальный». Примером может служить 1 миллион записей с именами производителей автомобилей, в результате чего может получиться 20 или 30 уникальных названий производителей.

4. Итак, я все еще не совсем понимаю, что вы хотите сделать. Теперь это звучит так: у вас 90 тысяч записей. В каждой записи есть, скажем, целый документ (скажем, сто слов, твит, короткий рассказ, что угодно). У вас есть словарь из N замен, например «foo»->»бар». Вы хотите выполнить замены во всех 90 тысячах документов ? Я пытаюсь заставить вас лучше объяснить вашу проблему; вы знаете, в чем ваша проблема, потому что вы ее видите, но вы нам ее не показали и не проделали большой работы по ее объяснению. Примеры данных и ожидаемый результат (отредактированный в вашем вопросе) помогут

5. Спасибо за комментарии. Запись содержит только одно слово, и многие записи содержат одно и то же слово, и в столбце может быть 20 уникальных слов. Цель состоит в том, чтобы в основном заменить каждое уникальное слово другим словом.

Ответ №1:

Итак, у вас есть свой словарь замен:

 Dim d as New Dictionary(Of String, String)
d("foo") = "bar"
d("baz") = "buf"
 

Вы можете применить их к столбцу ReplaceMe вашей таблицы:

 Dim rep as String = Nothing
For Each r as DataRow In dt.Rows
  If d.TryGetValue(r.Field(Of String)("ReplaceMe"), rep) Then r("ReplaceMe") = rep 
Next r
 

На моей машине требуется 340 мс для 1 миллиона замен. Я могу сократить это до 260 мс, используя номер столбца, а не имя — If d.TryGetValue(r.Field(Of String)(0), rep) Then r(0) = rep

Синхронизация:

     'setup, fill a dict with string replacements like "1" -> "11", "7" -> "17"
    Dim d As New Dictionary(Of String, String)
    For i = 0 To 9
        d(i.ToString()) = (i   10).ToString()
    Next

    'put a million rows in a datatable, randomly assign dictionary keys as row values
    Dim dt As New DataTable
    dt.Columns.Add("ReplaceMe")
    Dim r As New Random()
    Dim k = d.Keys.ToArray()
    For i = 1 To 1000000
        dt.Rows.Add(k(r.Next(k.Length)))
    Next

    'what range of values do we have in our dt?
    Dim minToMaxBefore = dt.Rows.Cast(Of DataRow).Min(Function(ro) ro.Field(Of String)("ReplaceMe")) amp; " - " amp; dt.Rows.Cast(Of DataRow).Max(Function(ro) ro.Field(Of String)("ReplaceMe"))

    'it's a crappy way to time, but it'll prove the point
    Dim start = DateTime.Now

    Dim rep As String = Nothing
    For Each ro As DataRow In dt.Rows
        If d.TryGetValue(ro.Field(Of String)("ReplaceMe"), rep) Then ro("ReplaceMe") = rep
    Next

    Dim ennd = DateTime.Now

    'what range of values do we have now
    Dim minToMaxAfter = dt.Rows.Cast(Of DataRow).Min(Function(ro) ro.Field(Of String)("ReplaceMe")) amp; " - " amp; dt.Rows.Cast(Of DataRow).Max(Function(ro) ro.Field(Of String)("ReplaceMe"))


    MessageBox.Show($"min to max before of {minToMaxBefore} became {minToMaxAfter} proving replacements occurred, it took {(ennd - start).TotalMilliseconds} ms for 1 million replacements")
 

введите описание изображения здесь

Комментарии:

1. Спасибо, этот подход невероятно (невероятно) быстр!