2010-09-21 16 views
5

powiedzmy mamy klasęRenderowanie hierarchii za pomocą LINQ?

Category 
{ 
    ID, 
    Name, 
    ParentID 
} 

oraz lista

1, 'Item 1', 0 
2, 'Item 2', 0 
3, 'Item 3', 0 
4, 'Item 1.1', 1 
5, 'Item 3.1', 3 
6, 'Item 1.1.1', 4 
7, 'Item 2.1', 2 

możemy przy użyciu LINQ do renderowania drzewa jak:

Item 1 
Item 1.1 
    Item 1.1.1 
Item 2 
Item 2.1 
Item 3 
Item 3.1 

Każda pomoc jest mile widziana!

Odpowiedz

5

Oto „LINQ "tylko" wersja:

Func<int, int, string[]> build = null; 
build = (p, n) => 
{ 
    return (from x in categories 
      where x.ParentID == p 
      from y in new[] 
      { 
       "".PadLeft(n)+ x.Name 
      }.Union(build(x.ID, n + 1)) 
      select y).ToArray(); 
}; 
var lines = build(0, 0); 

Tak, jest to rekurencyjny LINQ.


prośba Per NVA „s, oto sposób, aby wszystkie "sieroce" rekordy stają rekordy root:

Func<IEnumerable<int>, int, string[]> build = null; 
build = (ps, n) => 
{ 
    return (from x in categories 
      where ps.Contains(x.ParentID) 
      from y in new[] 
    { 
     "".PadLeft(n)+ x.Name 
    }.Union(build(new [] { x.ID }, n + 1)) 
      select y).ToArray(); 
}; 

var roots = (from c in categories 
      join p in categories on c.ParentID equals p.ID into gps 
      where !gps.Any() 
      orderby c.ParentID 
      select c.ParentID).Distinct(); 

var lines = build(roots, 0); 
+0

Wielkie dzięki za pomoc. Działa naprawdę dobrze ^^ – ByulTaeng

+0

Btw, jeśli ParentID elementu nie ma na liście, zostanie wykluczone, czy możesz mi pomóc jeszcze raz? Wielkie dzięki! – ByulTaeng

+0

Moja odpowiedź już wyklucza je, jeśli 'ParentID' nie znajduje się na liście. Czy prosisz o uwzględnienie go? A jeśli tak, czy chcesz, aby te "sieroty" wyświetlały się na poziomie głównym (tak jakby ich "ParentID" było "0")? – Enigmativity

0

Można użyć rekurencji:

public class Category 
{ 
    public int ID { get; set; } 
    public string Name { get; set; } 
    public int ParentID { get; set; } 
    public List<Category> Children { get; set; } 
} 

class Program 
{ 
    static void Main() 
    { 
     List<Category> categories = new List<Category>() 
     { 
      new Category() { ID = 1, Name = "Item 1", ParentID = 0}, 
      new Category() { ID = 2, Name = "Item 2", ParentID = 0 }, 
      new Category() { ID = 3, Name = "Item 3", ParentID = 0 }, 
      new Category() { ID = 4, Name = "Item 1.1", ParentID = 1 }, 
      new Category() { ID = 5, Name = "Item 3.1", ParentID = 3 }, 
      new Category() { ID = 6, Name = "Item 1.1.1", ParentID = 4 }, 
      new Category() { ID = 7, Name = "Item 2.1", ParentID = 2 } 
     }; 

     List<Category> hierarchy = new List<Category>();       
     hierarchy = categories 
         .Where(c => c.ParentID == 0) 
         .Select(c => new Category() { ID = c.ID, Name = c.Name, ParentID = c.ParentID, Children = GetChildren(categories, c.ID) }) 
         .ToList(); 

     HieararchyWalk(hierarchy);    

     Console.ReadLine(); 
    }   

    public static List<Category> GetChildren(List<Category> categories, int parentId) 
    {    
     return categories 
       .Where(c => c.ParentID == parentId) 
       .Select(c => new Category { ID = c.ID, Name = c.Name, ParentID = c.ParentID, Children = GetChildren(categories, c.ID) }) 
       .ToList(); 
    } 

    public static void HieararchyWalk(List<Category> hierarchy) 
    { 
     if (hierarchy != null) 
     { 
      foreach (var item in hierarchy) 
      { 
       Console.WriteLine(string.Format("{0} {1}", item.ID, item.Name)); 
       HieararchyWalk(item.Children);      
      } 
     } 
    }   
} 
4

Te metody przedłużania wiem dokładnie, co chcesz:

public static partial class LinqExtensions 
{ 
    public class Node<T> 
    { 
     internal Node() { } 

     public int Level { get; internal set; } 
     public Node<T> Parent { get; internal set; } 
     public T Item { get; internal set; } 
     public IList<Node<T>> Children { get; internal set; } 
    } 

    public static IEnumerable<Node<T>> ByHierarchy<T>(
     this IEnumerable<T> source, 
     Func<T, bool> startWith, 
     Func<T, T, bool> connectBy) 
    { 
     return source.ByHierarchy<T>(startWith, connectBy, null); 
    } 

    private static IEnumerable<Node<T>> ByHierarchy<T>(
     this IEnumerable<T> source, 
     Func<T, bool> startWith, 
     Func<T, T, bool> connectBy, 
     Node<T> parent) 
    { 
     int level = (parent == null ? 0 : parent.Level + 1); 

     if (source == null) 
      throw new ArgumentNullException("source"); 

     if (startWith == null) 
      throw new ArgumentNullException("startWith"); 

     if (connectBy == null) 
      throw new ArgumentNullException("connectBy"); 

     foreach (T value in from item in source 
          where startWith(item) 
          select item) 
     { 
      var children = new List<Node<T>>(); 
      Node<T> newNode = new Node<T> 
      { 
       Level = level, 
       Parent = parent, 
       Item = value, 
       Children = children.AsReadOnly() 
      }; 

      foreach (Node<T> subNode in source.ByHierarchy<T>(possibleSub => connectBy(value, possibleSub), 
                   connectBy, newNode)) 
      { 
       children.Add(subNode); 
      } 

      yield return newNode; 
     } 
    } 

    public static void DumpHierarchy<T>(this IEnumerable<Node<T>> nodes, Func<T, string> display) 
    { 
     DumpHierarchy<T>(nodes, display, 0); 
    } 

    private static void DumpHierarchy<T>(IEnumerable<LinqExtensions.Node<T>> nodes, Func<T, string> display, int level) 
    { 
     foreach (var node in nodes) 
     { 
      for (int i = 0; i < level; i++) Console.Write(" "); 
      Console.WriteLine (display(node.Item)); 
      if (node.Children != null) 
       DumpHierarchy(node.Children, display, level + 1); 
     } 
    } 

} 

Można ich używać w następujący sposób:

categories.ByHierarchy(
     cat => cat.ParentId == null, // assuming ParentId is Nullable<int> 
     (parent, child) => parent.Id == child.ParentId) 
    .DumpHierarchy(cat => cat.Name); 
0
public IEnumerable<HelpPageMenuItem> GetHelpPageMenuItems() 
    { 
     var helpPages = (from h in Context.HelpPages select new HelpPageMenuItem{HelpPageId = h.HelpPageId, ParentHelpPageId = h.ParentHelpPageId, PageContext = h.PageContext, MenuText = h.MenuText}).ToList(); 
     var parents = from h in helpPages where !h.ParentHelpPageId.HasValue select PopulateChildren(h, helpPages); 
     return parents.ToList(); 
    } 

    private static HelpPageMenuItem PopulateChildren(HelpPageMenuItem helpPageMenuItem, IEnumerable<HelpPageMenuItem> helpPages) 
    { 
     helpPageMenuItem.ChildHelpPages = 
      (from h in helpPages 
      where h.ParentHelpPageId == helpPageMenuItem.HelpPageId 
      select PopulateChildren(h, helpPages)).ToList(); 

     return helpPageMenuItem; 
    } 
0
@model List<OrgChart.Models.Node> 
    @{  

    Func<int?, List<OrgChart.Models.Node>, string> recuresive = null; 
recuresive = (parentid, list) => string.Join("", list.Where(x => x.ParentId == parentid).Select(x => "<li>" + x.Name + "<ul>" + recuresive(x.Id, list.Where(y => y.ParentId != parentid).ToList()) + "</ul></li>")); 
    } 
    @Html.Raw("<ul id='org1' >" + recuresive(null, Model) + "</ul>") 
    <div id="chart" class="orgChart"></div> 
0
 public static List<TSource> BuildTreeView<TSource, TKey>(this List<TSource> allItems 
     , Func<TSource, TKey> parentSelector, Func<TSource, TKey> childSelector, Expression<Func<TSource, List<TSource>>> childrenPropertySelector 
     , Func<TSource, bool> GetRoot, List<TSource> rootList = null) 
    { 

     if (rootList == null) 
      rootList = allItems.Where(GetRoot).ToList(); 
     if (rootList != null && rootList.Count > 0) 
     { 
      rootList.ForEach(rootItem => 
      { 
       Func<TSource, bool> whereClause = x => childSelector(rootItem).Equals(parentSelector(x)); 
       var childrenProperty = (childrenPropertySelector.Body as MemberExpression).Member as System.Reflection.PropertyInfo; 
       var childrenList = allItems.Where(whereClause).ToList(); 
       childrenProperty.SetValue(rootItem, childrenList); 

       if (childrenList.Count > 0) 
        BuildTreeView(allItems, parentSelector, childSelector, childrenPropertySelector, GetRoot, childrenProperty.GetValue(rootItem) as List<TSource>); 
      }); 

     } 
     return rootList; 
    } 

// wywołanie metody

List<Channel> rootChannel = listChannel.BuildTreeView(f => f.PARENT_CODE, x => x.CODE, z => z.SubChannels, c => c.CODE == "AC"); 
Powiązane problemy