2009-06-17 32 views
2

不确定是否需要更正此类问题的标签,但您对以下通用解决方案有任何想法吗?嵌套分组策略/算法c#

鉴于发票的集合:

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

我可以使用哪些技术,其中规定是这样的:

var tree = invoices.ToHeirarchy(t => { 
    t.GroupBy(x => x.Date); 
    t.GroupBy(x => x.Customer); 
}) 

结果的图形,如:

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 

,并允许以下要通过(允许从任何级别的发票计算)?

Assert.AreEqual(3, tree.Node[0].Items.Count) 
Assert.AreEqual(DateTime.Parse("1/1/2009"), tree.Node[0].Key) 

Assert.AreEqual(3, tree.Node[1].Items.Count) 
Assert.AreEqual(DateTime.Parse("1/2/2009"), tree.Node[1].Key) 

Assert.AreEqual("a", tree.Node[0].Node[0].Key) 
Assert.AreEqual(1, tree.Node[0].Node[0].Items.Count) 

Assert.AreEqual("b", tree.Node[0].Node[1].Key) 
Assert.AreEqual(2, tree.Node[0].Node[1].Items.Count) 
+0

您在投影方面寻找什么?匿名类型,预定义类型,一些通用的填充结构,如DataSet或Dictionary? – jpierson 2009-11-17 11:56:37

回答

0

取而代之的是树上的果子,你可以通过客户按日期&然后对结果进行排序。

1

远不是可重复使用的,但这应该这样做:

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

你需要一个类型,它是能够表示树形结构的。在框架中可以使用某些类型 - 例如KeyValuePair<TKey, TValue>,树视图节点TreeNode,XML元素XmlElementXElement,可能还有一些类型。以下示例包含使用XElement来表示树的两个解决方案。一个使用lambdas来访问成员,另一个使用字符串,都有优点和缺点。我认为可以通过复杂的代码从解决方案中获得最大收益。

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

第一个解决方案的输出如下。

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

该解决方案是远远不够完善,但可能提供的东西下手,他们给LINQ的全功率为XML查询树。如果您打算大量使用这些树,我建议为该树构建更适合需求的custome节点类型。但是设计这个可能会很难 - 尤其是如果你想要打字的话。

最后我想提一下,我不能真正看到使用这样的结构 - 使用LINQ来直接从列表中获取结果是不是很容易?

0

我还没有测试过,但我认为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()调用实际上是可选的,具体取决于您试图在投影中实现的内容。

最近我问了一个similar question,看来我可能会自己回答。请看看你是否认为它可能有助于理解其他与linq分组相关的选项来创建层次结构。