【问题标题】:How to GroupJoin from flat XDocument?如何从平面 XDocument 进行 GroupJoin?
【发布时间】:2012-01-29 11:54:33
【问题描述】:

我有一个 XDocument,其片段类似于:

<Data>
    <Row Id="0" ParentId="-1">
        <!-- stuff -->
    </Row>
    <Row Id="1" ParentId="0">
        <!-- stuff -->
    </Row>
    <Row Id="2" ParentId="0">
        <!-- stuff -->
    </Row>
    <Row Id="3" ParentId="-1">
        <!-- stuff -->
    </Row>
    <Row Id="4" ParentId="3">
        <!-- stuff -->
    </Row>
</Data>

假设嵌套仅限于上面的示例。 我想创建一个数据结构 - IDictionary&lt;Parent, List&lt;Child&gt;&gt;。我似乎无法正确加入任何东西。到目前为止,我所要做的是:

// get lists of data nodes
List<XElement> pRows = xData.Elements(XName.Get("Row"))
                            .Where(e => e.Attribute(XParentId).Value == "-1")
                            .Select(e => e)
                            .ToList();
List<XElement> cRows = xData.Elements(XName.Get("Row"))
                            .Where(e => e.Attribute(XParentId).Value != "-1")
                            .Select(e => e)
                            .ToList();

var dataSets = pRows.GroupJoin(cRows,
                               p => p,
                               c => c.Attribute(XParentId).Value,
                               (p, children) => new {
                                 ParentID = p.Attribute(XId).Value,
                                 Children = children.Select(c => c)
                               });

编译器在抱怨:

方法的类型参数 'System.Linq.Enumerable.GroupJoin(System.Collections.Generic.IEnumerable, System.Collections.Generic.IEnumerable, 系统函数,系统函数, System.Func,TResult>)' 不能从用法中推断出来。尝试指定类型参数 明确的。

我关注了来自MSDN using the GroupJoin 的样本。我不想使用 2 个列表 - 我更喜欢使用包含所有行的 List&lt;XElement&gt; 的单个列表。

【问题讨论】:

    标签: c# linq-to-xml


    【解决方案1】:

    我认为 2 列表方法更简洁,除非我会避免在最后一步或真正需要列表之前调用 ToList()。你可以把它变成一个陈述,但它会很长而且更难理解。

    要修复您的查询,您需要将pRows 的外部键选择器从p =&gt; p 更改为p =&gt; p.Attribute(XId).Value,这是实际的ID。目前它选择整个元素,无法与c =&gt; c.Attribute(XParentId).Value 进行比较,因为它们是不同的类型。更新后的查询将是:

    var dataSets = pRows.GroupJoin(cRows,
                                   p => p.Attribute(XId).Value,
                                   c => c.Attribute(XParentId).Value,
                                   (p, children) => new {
                                     ParentID = p.Attribute(XId).Value,
                                     Children = children.Select(c => c)
                                   });
    

    为避免使用 2 个列表,您可以修改上面的查询并将 pRowscRows 替换为它们各自的查询,但这会使它变得冗长且难看。在这种特殊情况下,我更喜欢使用查询语法来表达GroupJoin,因为它比流畅的语法更容易阅读:

    var query = from root in xData.Elements("Row").Where(e => e.Attribute("ParentId").Value == "-1")
                join child in xData.Elements("Row").Where(e => e.Attribute("ParentId").Value != "-1")
                on root.Attribute("Id").Value equals child.Attribute("ParentId").Value
                into rootChild
                select new 
                {
                    ParentId = root.Attribute("Id").Value,
                    Children = rootChild.Select(o => o)
                };
    
    var dict = query.ToDictionary(o => o.ParentId, o => o.Children.ToList());
    

    如果您的真正问题有更多嵌套,LINQ 可能不是理想的解决方案。

    【讨论】:

      【解决方案2】:

      首先,要编译您的代码,您可能希望将GroupJoin 中的参数p =&gt; p 替换为p =&gt; p.Attribute(XId).Value,它选择了比较键。

      因此,您将获得一个带有对象的 IEnumerable

      • ParentID=0, IEnumerable&lt;XElement&gt; { Row Id=1, Row Id=2 }
      • ParentID=3, IEnumerable&lt;XElement&gt; { Row Id=4 }

      当然,您也可以将 .Select(c =&gt; c) 更改为仅返回带有 ID (.Select(c =&gt; c.Attribute(XId).Value).ToList()) 的 List&lt;string&gt;,但您仍然缺少 ParentID=-1 而您没有没有字典。

      如果您想包含 ParentID=-1 并获得 Dictionary&lt;string,List&lt;string&gt;&gt;,那么您可能想尝试这个(作为起点),它使用不同的方法:

      // get all ParentIds
      var allParentIds = xData.Elements(XName.Get("Row"))
                              .Select(e => e.Attribute(XParentId).Value)
                              .Distinct();
      // then use them to get all Ids where the ParentId is in the "list" of all ParentIds
      var parentIdsAndChilds = from givenIds
                               in allParentIds
                               select new {
                                   Id = givenIds,
                                   Childs = xData.Elements(XName.Get("Row")).Where(e => e.Attribute(XParentId).Value == givenIds).Select(e => e.Attribute(XId).Value).ToList()
                               };
      // if needed, convert it to a Dictionary
      Dictionary<string, List<string>> dict = parentIdsAndChilds.ToDictionary(k => k.Id, v => v.Childs);
      

      我希望这会有所帮助,或者可能为进一步研究提供一个起点。

      附注:您可能知道,LINQ 使用 延迟执行,因此您可能不想在每一步都调用 ToList() 以节省执行时间(取决于数据总量,它不时使用ToList() 可能是个好主意,因为它可能会在处理过程中节省内存,但这取决于实际数据。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-01-14
        • 2020-04-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-11-02
        • 1970-01-01
        相关资源
        最近更新 更多