【问题标题】:Search and replace inside string column in DataTable is slow?在 DataTable 中的字符串列内搜索和替换很慢?
【发布时间】:2021-10-19 19:34:41
【问题描述】:

我在 DataTable (.dt) 的字符串列中获取不同的单词,然后用另一个值替换唯一值,因此基本上将单词更改为其他单词。下面列出的两种方法都有效,但是,对于 90k 记录,该过程不是很快。有没有办法加快这两种方法的速度?

第一种方法,如下:

   'fldNo is column number in dt
   For Each Word As String In DistinctWordList
      Dim myRow() As DataRow
      myRow = dt.Select(MyColumnName & "='" & Word & "'")
      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

【问题讨论】:

  • 定义“不是很快”?我对 LINQ “实际上不是很快”并不感到惊讶 - LINQ 更多的是简洁,而不是效率
  • 另外,你能更简单地定义你的问题吗?你的问题很难理解。听起来像“我有一个 90k 单词的列表,其中一些出现不止一次,而一些只出现一次。对于那些只出现一次的单词,我想将它们换成字典中的另一个单词已经查找了。90k 个单词在 90k 行数据表的一列中,每行一个单词“ - 对吗?
  • @Caius Jard - dt 中“有 90k 条记录”,而不是 90k 字。此外,已在此列中找到的不同单词列表由 DistintWordsList 给出,它是使用 LINQ 的 .Distinct() 构造的。您对“某些单词可能出现一次或多次”的担忧并不重要,因为唯一的单词是唯一的单词 - 如果一个单词出现一次或多次,它仍然是唯一的。不 - 每行没有一个单词,许多单词重复 - 因此术语是唯一的。该示例类似于 100 万条包含汽车制造商名称的记录 - 可能会产生 20 或 30 个唯一的制造商名称。
  • 所以,我还是不太清楚你想做什么。现在听起来像:您有 90k 条记录。例如,每条记录都有一个完整的文档(比如说一百个单词、一条推文、一个短篇小说等等)。你有一个包含 N 个替换的字典,例如“foo”->“bar”。您想在所有 90k 文档中执行替换吗?我试图让你更好地解释你的问题;你知道你的问题是什么,因为你可以看到它,但你没有向我们展示它,也没有很好地解释它。示例数据和您期望的输出(编辑到您的问题中)会有所帮助
  • 感谢 cmets。一条记录只保存一个词,很多记录有同一个词,列中可能有20个唯一词。目的是基本上用不同的词替换每个唯一的词。

标签: vb.net search replace datatable


【解决方案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

在我的机器上,100 万次替换需要 340 毫秒。我可以通过使用列号而不是名称将其缩短到 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")) & " - " & 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")) & " - " & 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")

【讨论】:

  • 谢谢,这种方法非常快(难以置信)!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-10
  • 2017-01-10
  • 2018-01-02
  • 1970-01-01
相关资源
最近更新 更多