【问题标题】:Linq - How to convert from flat to hierarchical?Linq - 如何从平面转换为分层?
【发布时间】:2010-11-30 00:47:59
【问题描述】:

我有以下作为 Linq 查询结果生成的结果集。我想将其转换为分层结果集。前两列代表“主”行,第 3 列和第 4 列代表主行的子列表,第 5 列和第 6 列代表主行的第二个子列表。包含值 1971 的列是连接列。

最终结果应该是一个包含容器列表(G2 列)和打印机列表(G3 列)的主对象。

将其转换为分层形式的查询是什么样的?

G1_ID   G1_CellName  G2_ContainerID G2_ID   G2_SerialNumber G3_ID      G3_PrinterName
1971    Default Cell    1935           1971    1101929         1971       PBG-PrtEmulator1
1971    Default Cell    1936           1971    1101930         1971       PBG-PrtEmulator1
1971    Default Cell    2189           1971    1102183         1971       PBG-PrtEmulator1

【问题讨论】:

  • 澄清一下...您想要一个看起来像对象的层次结构 - Cell > 带有 Cell.Containers、Cell.SerialNumbers、Cell.PrinterNames(作为集合)?
  • 这是 Linq to Sql、Linq to EF、Linq to objects 还是别的什么?
  • @Tom - 我想要一个看起来像 Cell -> Cell.Containers 和 Cell -> PrinterNames 的层次结构。序列号列是 Container 的一个属性。
  • @DoctaJonez - 直线 Linq。在执行 L2S 查询后,此查询将在客户端上执行。我会将结果转换为 IEnumerable,然后创建分层结果。

标签: linq


【解决方案1】:

分组?

var result = from eachData in data
group eachData by new{ eachData .G1_ID, eachData .G1_CellName }
into g1
from eachG1 in g1
group eachG1 by new { eachG1.G2_..., eachG1.G2_... }
into g2
for eachG2 in g2
group eachG2 by new { eachG2.G3_... }
into g3
select g3;

没有测试过。但我敢肯定它会看起来像这样。

【讨论】:

  • 我曾考虑过 GroupBy,但这不会产生分组到每个 g1 元素中的 g2 和 g3 数据的集合。我认为您必须在原始列表上通过 3 次,但也许我错了 :)
【解决方案2】:

好的,这是一个发人深省的问题。我过去做过很多数据扁平化,通常我会使用字典来保留所有唯一值,然后将它们结合起来。

您要求使用 LINQ,现在我想不出单次通过的方式来做到这一点,所以我在 VB 中得到了这个...

Private Class FlatObj
    Public Property G1_ID As Integer
    Public Property G1_CellName As String
    Public Property G2_ContainerID As Integer
    Public Property G2_ID As Integer
    Public Property G2_SerialNumber As Integer
    Public Property G3_ID As Integer
    Public Property G3_PrinterName As String
End Class

Private Class G1
    Public Property ID As Integer
    Public Property CellName As String
    Public Property Containers As New List(Of G2)()
    Public Property PrinterNames As New List(Of G3)()
    Public Overrides Function Equals(ByVal obj As Object) As Boolean
        Return ID.Equals(CType(obj, G1).ID)
    End Function
    Public Overrides Function GetHashCode() As Integer
        Return ID.GetHashCode()
    End Function
End Class

Private Class G2
    Public Property fID As Integer
    Public Property ContainerID As Integer
    Public Property SerialNumber As Integer
    Public Overrides Function Equals(ByVal obj As Object) As Boolean
        Return ContainerID.Equals(CType(obj, G2).ContainerID)
    End Function
    Public Overrides Function GetHashCode() As Integer
        Return ContainerID.GetHashCode()
    End Function
End Class

Private Class G3
    Public Property fID As Integer
    Public Property PrinterName As String
    Public Overrides Function Equals(ByVal obj As Object) As Boolean
        Return PrinterName.Equals(CType(obj, G3).PrinterName)
    End Function
    Public Overrides Function GetHashCode() As Integer
        Return PrinterName.GetHashCode()
    End Function
End Class

Dim fromDb As New List(Of FlatObj) From
    {
        New FlatObj() With {.G1_ID = 1971, .G1_CellName = "Default Cell", .G2_ContainerID = 1935, .G2_ID = 1971, .G2_SerialNumber = 1101929, .G3_ID = 1971, .G3_PrinterName = "PBG-PrtEmulator1"},
        New FlatObj() With {.G1_ID = 1971, .G1_CellName = "Default Cell", .G2_ContainerID = 1936, .G2_ID = 1971, .G2_SerialNumber = 1101930, .G3_ID = 1971, .G3_PrinterName = "PBG-PrtEmulator1"},
        New FlatObj() With {.G1_ID = 1971, .G1_CellName = "Default Cell", .G2_ContainerID = 2189, .G2_ID = 1971, .G2_SerialNumber = 1102183, .G3_ID = 1971, .G3_PrinterName = "PBG-PrtEmulator1"}
    }

Dim g1s = fromDb.Select(Function(x) New G1 With
                                    {
                                        .ID = x.G1_ID,
                                        .CellName = x.G1_CellName
                                    }).Distinct().ToList()
Dim g2s = fromDb.Select(Function(x) New G2 With
                                    {
                                        .fID = x.G2_ID,
                                        .ContainerID = x.G2_ContainerID,
                                        .SerialNumber = x.G2_SerialNumber
                                    }).Distinct().ToLookup(Function(x) x.fID)
Dim g3s = fromDb.Select(Function(x) New G3 With
                                    {
                                        .fID = x.G3_ID,
                                        .PrinterName = x.G3_PrinterName
                                    }).Distinct().ToLookup(Function(x) x.fID)
g1s.ForEach(Sub(g)
                g.Containers.AddRange(g2s(g.ID))
                g.PrinterNames.AddRange(g3s(g.ID))
            End Sub)

请注意,很多工作都通过 Distinct() 和 ToLookup() 扩展完成。希望这会有所帮助,我想看看是否有更“LINQy”的方式:D

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-06-20
    • 2022-11-19
    • 1970-01-01
    • 2012-03-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多