2009-06-17 4 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 또는 사전과 같은 일부 일반 채워진 구조? – 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을 사용하여 트리를 나타내는 두 가지 솔루션이 포함되어 있습니다. 하나는 람다를 사용하여 멤버에 액세스하고 다른 하나는 문자열을 사용하며 장단점이 있습니다. 복잡한 코드로 솔루션에서 최상의 결과를 얻을 수 있다고 가정합니다.

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> 

이 솔루션은 지금까지 완벽한에서하지만 시작하는 무언가를 제공 할 수 있으며 그들은 나무를 조회하는 XML에 LINQ의 모든 기능을 제공합니다. 이 트리를 많이 사용하려는 경우 필요에 더 적합한 트리의 커스텀 노드 유형을 작성하는 것이 좋습니다. 그러나 이것을 설계하는 것은 매우 어려울 것입니다 - 특히 강력한 타이핑을 원할 경우.

마지막으로 나는 이러한 구조의 사용을 실제로 볼 수 없다는 것을 언급하고자합니다. 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을 사용하여 그룹을 만들어 계층 구조를 만들 때 다른 옵션을 이해하는 데 도움이된다고 생각한다면보십시오.