#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. Спасибо, этот подход невероятно (невероятно) быстр!