2010-05-03 4 views
1

이 프로그래밍 연습을 설정했습니다.1 LINQ 문을 사용하여 계층 적 데이터에서 트리 채우기

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 

namespace ConsoleApplication2 
{ 

    class DataObject { 
     public int ID { get; set; } 
     public int ParentID { get; set; } 
     public string Data { get; set; } 
     public DataObject(int id, int pid, string data) { this.ID = id; this.ParentID = pid; this.Data = data; } 
    } 

    class TreeNode { 
     public DataObject Data {get;set;} 
     public List<DataObject> Children { get; set; } 
    } 


    class Program 
    { 

     static void Main(string[] args) 
     { 
      List<DataObject> data = new List<DataObject>(); 
      data.Add(new DataObject(1, 0, "Item 1")); 
      data.Add(new DataObject(2, 0, "Item 2")); 
      data.Add(new DataObject(21, 2, "Item 2.1")); 
      data.Add(new DataObject(22, 2, "Item 2.2")); 
      data.Add(new DataObject(221, 22, "Item 2.2.1")); 
      data.Add(new DataObject(3, 0, "Item 3")); 

     } 
    } 
} 

원하는 출력 등등 2 개 아이 부재와 같은 데이터 객체의리스트를 가질 것이다 항목 1, 2 및 3 항목을 갖는 2, 3 개의 TreeNode의리스트이다.

저는 LINQ에서 단 하나의 문을 사용하여이 트리 (또는 포리스트)를 채우려고했습니다. 간단한 그룹을 통해 원하는 데이터를 얻을 수 있지만 문제는 TreeNode 객체로 구성하는 것입니다.

누군가가 이에 대한 힌트 또는 불가능 결과를 줄 수 있습니까? 당신은 두 수준의 나무를 원하는 경우

+0

2 레벨 또는 임의의 레벨 수를 가진 나무 목록을 원한다고 말하고 있습니까? TreeNode 클래스를 정의한 방법으로 루트와 바로 밑의 자식 두 수준 만있는 트리를 사용할 수 있습니다. –

+0

예. 나는 임의의 깊이가 필요해. 나는 코드 샘플을 재현 할 때 그것을 놓쳤다 고 생각한다. – Midhat

답변

1

LINQ는 재귀 데이터 구조와 특히 잘하지 않습니다,하지만 당신은 가까이 갈 수 있습니다.

아마도 임의의 깊이의 트리가 필요하므로 Children에 대한 정의를 변경하고 트리 생성 LINQ 문을 만들기 위해 필요한 재귀를 수행하는 도우미 메서드를 생성자에 추가합니다.

public static TreeNode BuildTree(IEnumerable<DataObject> items, int rootId) 
{ 
    var root = new TreeNode(new DataObject(rootId, int.MinValue, "Root")); 
    return (from i in items 
      let n = new TreeNode(i) 
      group n by n.Data.ParentID into nodeGroup 
      orderby nodeGroup.Key ascending 
      select nodeGroup) 
      .Aggregate(root, (parent, childGroup) => 
      { 
       parent.DescendantsAndSelf() 
         .First(n => n.Data.ID == childGroup.Key) 
         .Children.AddRange(childGroup); 
       return parent; 
      }); 
} 

이 방법은 몇 가지 가정을 만들지 만, 올바른 방향으로 당신을 얻을해야합니다

class TreeNode 
{ 
    public DataObject Data { get; set; } 
    public List<TreeNode> Children { get; private set; } 

    public TreeNode(DataObject data) 
    { 
     Data = data; 
     Children = new List<TreeNode>(); 
    } 

    //name chosen to match XElement method. I would name this 
    //SelfAndDescendants or change the behavior to match the name. 
    public IEnumerable<TreeNode> DescendantsAndSelf() 
    { 
     return (new TreeNode[] { this }).Concat(from c in this.Children 
               from sc in c.DescendantsAndSelf() 
               select sc); 
    } 
} 

이제 우리는 다음과 같은 방법을 정의 할 수 있습니다.

  • 전달 된 요소 중 트리의 루트 노드는 없으며 루트 노드가 반환 값으로 생성됩니다.
  • 부모 노드의 ID는 노드의 id보다 항상 낮게 인에 속하는 메소드에 전달 된 순서에
  • 모든 항목 (이 OrderBy + Aggregate 트리에 항목을 밀어 호출 할 수 있습니다) 루트 또는 다른 항목 중 하나 (그렇지 않으면 예외는 .First() 호출에 의해 throw됩니다).
0

, 그것은 오히려 간단합니다

var roots = from root in data 
where !data.Any(d => root.ParentID == d.ID) 
select new TreeNode 
{ 
    Data = root, 
    Children = data.Where(d => d.ParentID == root.ID).ToList(), 
}