嵌套分组策略/算法c#
不确定我是否必须为这类问题更正标签,但您对以下问题的通用解决方案有何想法 给定发票集合:嵌套分组策略/算法c#,c#,algorithm,data-structures,hierarchical-grouping,C#,Algorithm,Data Structures,Hierarchical Grouping,不确定我是否必须为这类问题更正标签,但您对以下问题的通用解决方案有何想法 给定发票集合: var invoices = new List<Invoice>() { new Invoice() { Id = 1, Customer = "a", Date = DateTime.Parse("1/1/2009") }, new Invoice() { Id = 2, Customer = "a", Date = DateTime.Parse("1/2/2009") }, new Invo
var invoices = new List<Invoice>()
{
new Invoice() { Id = 1, Customer = "a", Date = DateTime.Parse("1/1/2009") },
new Invoice() { Id = 2, Customer = "a", Date = DateTime.Parse("1/2/2009") },
new Invoice() { Id = 3, Customer = "a", Date = DateTime.Parse("1/2/2009") },
new Invoice() { Id = 4, Customer = "b", Date = DateTime.Parse("1/1/2009") },
new Invoice() { Id = 5, Customer = "b", Date = DateTime.Parse("1/1/2009") },
new Invoice() { Id = 6, Customer = "b", Date = DateTime.Parse("1/2/2009") }
}
结果显示如下图形:
Date "1/1/2009"
Customer a
Invoice 1
Customer b
Invoice 4
Invoice 5
Date "1/2/2009"
Customer a
Invoice 2
Invoice 3
Customer b
Invoice 6
并且还允许通过以下各项(允许从任何级别计算发票)
您可以先按日期,然后按客户对结果进行排序,而不是按树进行排序。远不能重复使用,但这应该可以做到:
var tree = invoices.GroupBy(x => x.Date).Select(x => new
{
Key = x.Key,
Items = x.GroupBy(y => y.Customer).Select(y => new
{
Key = y.Key,
Items = y.Select(z => z.Id).ToList()
})
}).ToList();
您要查找的术语是“嵌套groupby”
您需要一个能够表示树结构的类型。框架中有一些类型可以使用,例如
KeyValuePair
、树视图节点TreeNode
、XML元素xmlement
和XElement
,可能还有更多。以下示例包含两个使用XElement
表示树的解决方案。一个使用lambda访问成员,另一个使用字符串,两者都有优点和缺点。我假设有可能从使用complexer代码的解决方案中获得最佳效果
static void Main()
{
IEnumerable<Invoice> invoices = new List<Invoice>()
{
new Invoice() { Id = 1, Customer = "a", Date = DateTime.Parse("1/1/2009") },
new Invoice() { Id = 2, Customer = "a", Date = DateTime.Parse("1/2/2009") },
new Invoice() { Id = 3, Customer = "a", Date = DateTime.Parse("1/2/2009") },
new Invoice() { Id = 4, Customer = "b", Date = DateTime.Parse("1/1/2009") },
new Invoice() { Id = 5, Customer = "b", Date = DateTime.Parse("1/1/2009") },
new Invoice() { Id = 6, Customer = "b", Date = DateTime.Parse("1/2/2009") }
};
StringBuilder sb = new StringBuilder();
TextWriter tw = new StringWriter(sb);
using (XmlWriter xmlWriter = new XmlTextWriter(tw) { Formatting = Formatting.Indented })
{
XElement t1 = new XElement("Root", BuildTree(invoices, i => i.Customer, i => i.Date, i => i.Id));
XElement t2 = new XElement("Root", BuildTree(invoices, "Customer", "Date", "Id"));
var xyz = t2.Elements("Customer").ElementAt(1).Descendants("Item").Count();
t1.WriteTo(xmlWriter);
t2.WriteTo(xmlWriter);
}
Console.WriteLine(sb.ToString());
Console.ReadLine();
}
public static IEnumerable<XElement> BuildTree<T>(IEnumerable<T> collection, params Func<T, Object>[] groups)
{
if ((groups != null) && (groups.Length > 0))
{
return collection
.GroupBy(groups[0])
.Select(grp => new XElement(
"Group",
new XAttribute("Value", grp.Key),
BuildTree(grp, groups.Skip(1).ToArray())));
}
else
{
return collection.Select(i => new XElement("Item"));
}
}
public static IEnumerable<XElement> BuildTree<T>(IEnumerable<T> collection, params String[] groups)
{
if ((groups != null) && (groups.Length > 0))
{
return collection
.GroupBy(i => typeof(T).GetProperty(groups[0]).GetValue(i, null))
.Select(grp => new XElement(
groups[0],
new XAttribute("Value", grp.Key),
BuildTree(grp, groups.Skip(1).ToArray())));
}
else
{
return collection.Select(i => new XElement("Item"));
}
}
static void Main()
{
IEnumerable发票=新列表()
{
新发票(){Id=1,Customer=“a”,Date=DateTime.Parse(“1/1/2009”)},
新发票(){Id=2,Customer=“a”,Date=DateTime.Parse(“1/2/2009”)},
新发票(){Id=3,Customer=“a”,Date=DateTime.Parse(“1/2/2009”)},
新发票(){Id=4,Customer=“b”,Date=DateTime.Parse(“1/1/2009”)},
新发票(){Id=5,Customer=“b”,Date=DateTime.Parse(“1/1/2009”)},
新发票(){Id=6,Customer=“b”,Date=DateTime.Parse(“1/2/2009”)}
};
StringBuilder sb=新的StringBuilder();
TextWriter tw=新的StringWriter(sb);
使用(XmlWriter XmlWriter=newXMLTextWriter(tw){Formatting=Formatting.Indented})
{
XElement t1=新XElement(“根”,构建树(发票,i=>i.Customer,i=>i.Date,i=>i.Id));
XElement t2=新XElement(“根”,构建树(发票,“客户”,“日期”,“Id”);
var xyz=t2.Elements(“客户”).ElementAt(1).子体(“项目”).Count();
t1.WriteTo(xmlWriter);
t2.WriteTo(xmlWriter);
}
Console.WriteLine(sb.ToString());
Console.ReadLine();
}
公共静态IEnumerable构建树(IEnumerable集合,参数Func[]组)
{
如果((groups!=null)&&(groups.Length>0))
{
回收
.GroupBy(组[0])
.选择(grp=>new XElement(
“集团”,
新XAttribute(“值”,grp.Key),
BuildTree(grp,groups.Skip(1.ToArray());
}
其他的
{
返回集合。选择(i=>newxelement(“项”);
}
}
公共静态IEnumerable构建树(IEnumerable集合,参数字符串[]组)
{
如果((groups!=null)&&(groups.Length>0))
{
回收
.GroupBy(i=>typeof(T).GetProperty(组[0]).GetValue(i,null))
.选择(grp=>new XElement(
组[0],
新XAttribute(“值”,grp.Key),
BuildTree(grp,groups.Skip(1.ToArray());
}
其他的
{
返回集合。选择(i=>newxelement(“项”);
}
}
第一个解决方案的输出如下所示
<Root>
<Group Value="a">
<Group Value="2009-01-01T00:00:00">
<Group Value="1">
<Item />
</Group>
</Group>
<Group Value="2009-02-01T00:00:00">
<Group Value="2">
<Item />
</Group>
<Group Value="3">
<Item />
</Group>
</Group>
</Group>
<Group Value="b">
<Group Value="2009-01-01T00:00:00">
<Group Value="4">
<Item />
</Group>
<Group Value="5">
<Item />
</Group>
</Group>
<Group Value="2009-02-01T00:00:00">
<Group Value="6">
<Item />
</Group>
</Group>
</Group>
</Root>
<Root>
<Customer Value="a">
<Date Value="2009-01-01T00:00:00">
<Id Value="1">
<Item />
</Id>
</Date>
<Date Value="2009-02-01T00:00:00">
<Id Value="2">
<Item />
</Id>
<Id Value="3">
<Item />
</Id>
</Date>
</Customer>
<Customer Value="b">
<Date Value="2009-01-01T00:00:00">
<Id Value="4">
<Item />
</Id>
<Id Value="5">
<Item />
</Id>
</Date>
<Date Value="2009-02-01T00:00:00">
<Id Value="6">
<Item />
</Id>
</Date>
</Customer>
</Root>
第二种解决方案产生以下结果
<Root>
<Group Value="a">
<Group Value="2009-01-01T00:00:00">
<Group Value="1">
<Item />
</Group>
</Group>
<Group Value="2009-02-01T00:00:00">
<Group Value="2">
<Item />
</Group>
<Group Value="3">
<Item />
</Group>
</Group>
</Group>
<Group Value="b">
<Group Value="2009-01-01T00:00:00">
<Group Value="4">
<Item />
</Group>
<Group Value="5">
<Item />
</Group>
</Group>
<Group Value="2009-02-01T00:00:00">
<Group Value="6">
<Item />
</Group>
</Group>
</Group>
</Root>
<Root>
<Customer Value="a">
<Date Value="2009-01-01T00:00:00">
<Id Value="1">
<Item />
</Id>
</Date>
<Date Value="2009-02-01T00:00:00">
<Id Value="2">
<Item />
</Id>
<Id Value="3">
<Item />
</Id>
</Date>
</Customer>
<Customer Value="b">
<Date Value="2009-01-01T00:00:00">
<Id Value="4">
<Item />
</Id>
<Id Value="5">
<Item />
</Id>
</Date>
<Date Value="2009-02-01T00:00:00">
<Id Value="6">
<Item />
</Id>
</Date>
</Customer>
</Root>
这种解决方案远非十全十美,但可能会提供一些东西作为起点,它们为查询树提供了LINQtoXML的全部功能。如果要大量使用此树,我建议为该树构建一个更适合需要的自定义节点类型。但是这可能很难设计——特别是如果你想要强输入的话
最后,我想说的是,我真的看不到这种结构的用途——使用LINQ to object直接从列表中获得结果不是更容易吗?我没有测试过它,但我认为eulerfx提出的答案是正确的。下面,我用LINQ理解语法为这类事情编写了自己的解决方案
var tree =
(from i in invoices
group i by i.Date into g1
select new
{
Key = g1.Key,
Items =
(from d in g1
group d by d.Customer into g2
select new
{
Key = g2.Key,
Items =
from d in g2
select new
{
Key = d.Id,
}
}).ToList()
}).ToList();
ToList()调用实际上是可选的,这取决于您在投影中试图实现的目标
最近我问了一个问题,似乎我自己也在回答。如果您认为它有助于理解使用linq分组以创建层次结构的其他选项,请查看。您在投影方面需要什么?匿名类型、预定义类型、一些通用填充结构(如数据集或字典)?