【问题标题】:Tricky LINQ query: return string that contain substring of the max length substring棘手的 LINQ 查询:返回包含最大长度子字符串的子字符串的字符串
【发布时间】:2016-03-28 04:52:00
【问题描述】:

我有 2 个数据表,每个数据表有 300,000 行(从 2 个带有 OLEDB 的 excel 工作表导入的数据表)。

第一个数据表是“dtTosearch”,第二个数据表是“sourceDt”。

这是 2 个表的示例:

我需要为“未标记”列 (sourceDt) 中的每一行找到与“令牌”列 (dtTosearch) 中每一行的匹配项。匹配条件为:

  1. “站点”值 =“站点”值
  2. 'cat' 值 = 'category' 值
  3. 未标记的值必须包含标记值
  4. 如果上述所有条件都存在多于一个匹配项,则查询必须返回具有最大长度的标记的匹配项(这是我不知道如何执行的棘手部分 linq)
  5. 这个任务必须在最短的处理时间内运行 - 因为它的要求,因为它更专业,而且因为我和朋友-同事-JAVA 热情的开发人员打赌,.NET 会运行得更快(就像白天一样 (-:)

我添加了代码的相关部分,它工作正常,但不是我想要的方式,我想缩短处理时间,查看 linqQuery() 函数中的 foreach 循环 - 如果你能帮助我,我将不胜感激通过将我的查询扩展到条件号 4 来替换该循环,该循环针对条件 4 运行,因为 linq 查询的结果按“令牌”长度按降序排列,因此它将退出并返回具有最大行长度的结果.

Private Sub startScanning()
    Dim siteNum As Double
    Dim categoryNum As Double
    Dim stringToSearchin As String

    For i = 0 To sourceDt.Rows.Count - 1
        siteNum = sourceDt.Rows(i).Item(0)
        categoryNum = sourceDt.Rows(i).Item(1)
        stringToSearchin = sourceDt.Rows(i).Item(3)
       Debug.WriteLine( linqQuery(siteNum, categoryNum, stringToSearchin) & " " &
           stringToSearchin)
    Next
End Sub

Private Function linqQuery(ByVal sitenum As Double, ByVal cat As Double,
                           ByVal stringToSearchIn As String) As String

    Dim query = From row In dtTosearch
                Where (row.Field(Of Double?)("site") = sitenum And
                row.Field(Of Double?)("category") = cat)
                Order By row.Field(Of String)("token").Length Descending
                Select New With {
                    .token = row.Field(Of String)("token")
         }


    For Each x In query
        If stringToSearchIn.Contains(x.token) Then
            Return x.token
            Exit Function
        End If
    Next

    Return ""
End Function

非常感谢您的时间和考虑!

【问题讨论】:

  • 嗨 @gnicolas 我也标记了 C#,因为如果示例是 C# 或 VB,它对我来说很重要
  • 如果伪代码没问题:我想你需要在这里加入一个小组。您希望将 table1 中的每条记录与 table2 中的许多记录连接起来。 var joinedTables = from r1 in table1 join r2 in table2 on new {r1.site, r1.cat , r1.untagged.Contains(r2.token)} equals new {r2.site, r2.category, true} into possibleMatches select new {row1 = r1, row2 = possibleMatches.MaxBy(p => p.token.Length)} 然后你有一个平面 ienumerable 你的加入记录。对不起,我在移动设备上的格式。
  • LINQ 连接将比 For 循环快得多。 For 循环变体导致将一个表的每一行与另一个表的每一行进行 O(m*n) 比较。另一方面,连接在内部使用哈希表,将成本降低到大约 O(n)。
  • tnx @gnicolas 我理解这个概念,对我来说 linq 的问题是我总是在语法上苦苦挣扎,我会尝试实现你的建议。

标签: vb.net linq


【解决方案1】:

在 C# 代码中你可以找到你想要的记录

from r1 in sourceDt.AsEnumerable()
join r2 in dtToSearch.AsEnumerable() 
    on new { p1 = r1.Field<int>("cat"), p2 = r1.Field<int>("Site") }
    equals new { p1 = r2.Field<int>("category"), p2 = r2.Field<int>("site") }
    into r2g
select new
{
    r1,
    p2 = (from r2 in r2g
          let token = r2.Field<string>("token")
          where token.Contains(r1.Field<string>("untagged"))
          orderby token.Length descending, token
          select r2).FirstOrDefault()
}

也就是说,首先group join数据表在它们必须有共同的字段上。 join - into 语法使其成为组加入,如 here 所述。

然后,在一组匹配的行中,过滤掉其tokens中包含untagged值的行,并返回具有最长token的行(也是按字母顺序排列的第一个,以防万一长度的关系)。

【讨论】:

  • 非常感谢@Gert,感谢您提供代码、解释以及指向您关于 groupjoin 的答案的链接,这正是我想要的。
  • 这是正确的答案,但有一点需要注意:先做一个 orderby 然后 firstordefault 是对整个列表进行排序以获得最大值(这可能对这里的性能无关紧要,但值得注意)。 “正确”的解决方案是一个通用扩展方法 MaxBy,就像在 moreLINQ 中找到的那样。
  • @gnicholas 我认为大多数时候每行只排序一个小列表,即连接项目的列表(示例中的 4 个项目)。但由于这是一个速度挑战,所以 OP 可能想尝试一下。
猜你喜欢
  • 1970-01-01
  • 2015-12-27
  • 1970-01-01
  • 2016-11-20
  • 2020-08-19
  • 2018-07-19
  • 1970-01-01
  • 2018-11-11
  • 1970-01-01
相关资源
最近更新 更多