2009-06-17 16 views
2

このタイプの問題のラベルを修正する必要があるかどうかはわかりませんが、以下の一般的な解決策に関する考えはありますか?ネストされたグループ化戦略/アルゴリズムC#

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.ToHeirarchy(t => { 
    t.GroupBy(x => x.Date); 
    t.GroupBy(x => x.Customer); 
}) 

結果:ようなものを指定するところ、私が使用することができますどのような技術

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

:請求書のコレクションを考えると

次のようにして渡してください(どのレベルからの請求書の計算も可能です)?代わりに木の

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

答えて

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を使用する2つのソリューションが含まれています。 1つはメンバーにアクセスするためにラムダを使用し、もう1つは文字列を使用し、両方とも賛否両論があります。私は、複雑なコードを持つソリューションから最高のものを得ることは可能だと思います。

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> 

第2の解決法は、以下をもたらす。

<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 to XMLの機能をすべて備えています。このツリーを頻繁に使用する予定の場合は、ニーズに合ったツリーのカスタムノードタイプを作成することをお勧めします。しかし、これを設計することはおそらく非常に難しくなります。特に強いタイピングが必要な場合は特にそうです。

最後に、このような構造の使用を実際に見ることはできません.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でグループ化して階層を作成する際の他のオプションを理解するのに役立つと思ったら見てください。

関連する問題