我有一个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<Parent, List<Child>>
。我似乎无法正确加入任何东西。我现在要说的是:
// 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,System.Func, System.Func,TResult&GT)” 无法从使用中推断出来。尝试指定类型参数 明确。
我跟踪了MSDN using the GroupJoin的样本。我不想使用2个列表 - 我更喜欢使用包含所有行的List<XElement>
的单个列表。
答案 0 :(得分:1)
我认为2列表方法更简洁,但我会避免在最后一步或真正需要列表之前调用ToList()
。你可以将它变成一个陈述,但这将是漫长而艰难的。
要修复您的查询,您需要将pRows
的外键选择器从p => p
更改为p => 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个列表,您可以修改上面的查询,并将pRows
和cRows
替换为各自的查询,但这会让您的眼睛变得越来越难。在这种特殊情况下,我更喜欢使用查询语法表达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可能不是理想的解决方案。
答案 1 :(得分:0)
首先,要编译代码,您可能希望将p => p
中的参数GroupJoin
替换为p => p.Attribute(XId).Value
,选择要进行比较的密钥。
因此,您将获得一个带有对象的IEnumerable
IEnumerable<XElement>
{Row Id = 1,Row Id = 2} IEnumerable<XElement>
{Row Id = 4} 当然,您也可以更改.Select(c => c)
以仅使用ID(List<string>
)返回.Select(c => c.Attribute(XId).Value).ToList()
,但您仍然缺少 ParentID = -1 强>你没有字典。
如果你想包括ParentID = -1并且还得到一个Dictionary<string,List<string>>
,那么你可能想尝试这个(作为开始点),它使用不同的方法:
// 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()
可能是一个好主意,因为它可能会在处理过程中节省内存,但这取决于实际数据。)