如何从平面 XDocument 进行 GroupJoin?

发布于 2024-12-23 17:18:21 字数 1901 浏览 0 评论 0 原文

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

<前><代码><数据>; <行 ID="0" ParentId="-1"> <行 ID="1" ParentId="0"> <行 ID="2" ParentId="0"> <行 ID="3" ParentId="-1"> <行 ID="4" ParentId="3">

假设嵌套仅限于上面的示例。 我想创建一个数据结构 - IDictionary>。我似乎无法正确加入任何东西。到目前为止我所知道的是:

// 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, 系统.Func,系统.Func, System.Func,TResult>)' 无法从用法推断。尝试指定类型参数 明确地。

我按照 MSDN 使用 GroupJoin 中的示例进行操作。我不想使用 2 个列表 - 我更喜欢使用包含所有行的 List 的单个列表。

I have an XDocument with a fragment that resembles:

<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>

Assume nesting is limited to the example above.
I want to create a data structure - IDictionary<Parent, List<Child>>. I can't seem to get anything to join correctly. What I have to this point is:

// 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)
                               });

The compiler is complaining:

The type arguments for method
'System.Linq.Enumerable.GroupJoin(System.Collections.Generic.IEnumerable,
System.Collections.Generic.IEnumerable,
System.Func, System.Func,
System.Func,TResult>)'
cannot be inferred from the usage. Try specifying the type arguments
explicitly.

I followed a sample from MSDN using the GroupJoin. I didn't want to use 2 lists - I'd prefer to use a single list of List<XElement> containing all the rows.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

樱&纷飞 2024-12-30 17:18:21

我认为 2 列表方法更干净,除非我会避免调用 ToList() 直到最后一步或真正需要列表时。你可以把它变成一个声明,但它会很长而且很难理解。

要修复您的查询,您需要将 pRows 的外部键选择器从 p => 更改为pp => p.Attribute(XId).Value,这是实际的ID。目前它选择整个元素,这不能与 c => 进行比较。 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 可能不是理想的解决方案。

I think the 2 list approach is cleaner, except I would avoid calling ToList() until the final step or when the list is really needed. You could turn it into one statement, but it would be long and harder to follow.

To fix your query, you need to change the outer key selector for pRows from p => p to p => p.Attribute(XId).Value, which is the actual ID. Currently it selects the entire element, which cannot be compared to c => c.Attribute(XParentId).Value since they are different types. The updated query would be:

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)
                               });

To avoid using 2 lists you could modify the query above and replace pRows and cRows with their respective queries, but that makes it long and harder on the eyes. In this particular case I prefer expressing the GroupJoin using query syntax since it is much easier to read than the fluent syntax:

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());

Should your real problem have more nesting, LINQ probably won't be the ideal solution.

还不是爱你 2024-12-30 17:18:21

首先,要使代码编译,您可能需要替换参数 p =>; GroupJoin 中的 pp =>; p.Attribute(XId).Value 选择用于比较的键。

结果,您将获得一个对象为

  • ParentID=0, IEnumerable { Row Id=1, Row Id=2 }
  • ParentID=3, IEnumerable 的 IEnumerable > { Row Id=4 }

当然,你也可以更改 .Select(c => c) 返回一个List 仅包含 ID (.Select(c => c.Attribute(XId).Value).ToList()),但您仍然缺少ParentID=-1 并且您没有字典。

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

// 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() 可能是个好主意,因为它可能会在处理过程中节省内存,但这取决于实际数据)。

First, to make your code compile you might want to replace the parameter p => p in the GroupJoin with p => p.Attribute(XId).Value which selects the key for comparison.

As a result you will get an IEnumerable with objects

  • ParentID=0, IEnumerable<XElement> { Row Id=1, Row Id=2 }
  • ParentID=3, IEnumerable<XElement> { Row Id=4 }

Of course, you can also change the .Select(c => c) to return a List<string> with just the IDs (.Select(c => c.Attribute(XId).Value).ToList()), but you are still missing ParentID=-1 and you don't have a dictionary.

If you want to include the ParentID=-1 and also get a Dictionary<string,List<string>> then you might want to try this (as a point to start from), which uses a different approach:

// 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);

I hope this helps, or maybe provides a starting point for further research.

One side note: as you might know, LINQ uses deferred execution so you might not want to call ToList() on every step to save execution time (depending on the overall amount of data, it might be a good idea to use ToList() anyway now and then because it might save memory during processing, but this depends on the real data).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文