簡體   English   中英

在LINQ中訂購自引用層次結構

[英]Ordering a self-referencing hierarchy in LINQ

我有一個名為NavigationElement的類,看起來像這樣

public class NavigationElement
{
  public int Id { get; set; }
  public string Title { get; set; }
  public string Link { get; set; }
  public int SortOrder { get; set; }
  public bool Visible { get; set; }
  public int? ParentId { get; set; }

  public virtual ICollection<NavigationElement> Children { get; set; }
  public virtual NavigationElement Parent { get; set; }

  public NavigationElement()
  {
    Children = new List<NavigationElement>();
  }
}

如您所見,該類是自引用的。 由此,我創建了一個帶有下拉菜單(正在播放層次結構)的站點導航菜單。

我正在努力訂購這些物品。 我希望頂層項目由SortOrder屬性排序,但是下面的所有內容都希望由Title屬性按字母順序排序

這就是我到目前為止所做的原因。

var orderedModel = unorderedModel.OrderBy(x => x.SortOrder).ThenBy(x => x.Children.OrderBy(y => y.Title).ThenBy(z => z.Children.OrderBy(a => a.Title))).ToList();

unorderedModel的類型為List<NavigationElementModel>

這正在編譯,但是運行代碼時出現錯誤。 錯誤提示:

至少一個對象必須實現IComparable。

您應該只遍歷所有子元素並將其排序。

有點像:

var ordered = unorderedModel.OrderBy(x=>x.SortOrder).ToList();
ordered.ForEach(OrderChildren);

public void OrderChildren(NavigationElement el)
{
    el.Children = el.Children.OrderBy(x => x.Title).ToList();
    if (el.Children != null)
    {
        foreach (var c in el.Children)
        {
            OrderChildren(c);
        }
    }
}

那這樣的東西呢?

public static class LinqExtension {
   public static IEnumerable<T> SelectManyRecursive<T>(this IEnumerable<T> source, Func<T, IEnumerable<T>> childrenSelector) {
       if (source == null) throw new ArgumentNullException("source");

       foreach (var i in source) {
           yield return i;

           var children = childrenSelector(i);
           if (children == null) continue;

           foreach (var child in SelectManyRecursive(children, childrenSelector)) {
               yield return child;
           }
       }
   }
}

var orderedModel = unorderedModel
        .OrderBy(x => x.SortOrder)
        .SelectMany(x => new[] { x }.Union(
                x.Children.SelectManyRecursive(y => y.Children)
                        .OrderBy(y => y.Parent.Title) // considering hierarchy
                        .ThenBy(y => y.Title)
                ))
        .ToList();

對於您的案例,我將使用Linq使用來自帶有路徑和深度字段的“排序”層次結構中的方法。

首先,我對如何通過LINQ展平樹的回答中的一般樹遍歷助手

public static partial class TreeUtils
{
    public static IEnumerable<T> Expand<T>(this IEnumerable<T> source, Func<T, IEnumerable<T>> elementSelector)
    {
        var stack = new Stack<IEnumerator<T>>();
        var e = source.GetEnumerator();
        try
        {
            while (true)
            {
                while (e.MoveNext())
                {
                    var item = e.Current;
                    yield return item;
                    var elements = elementSelector(item);
                    if (elements == null) continue;
                    stack.Push(e);
                    e = elements.GetEnumerator();
                }
                if (stack.Count == 0) break;
                e.Dispose();
                e = stack.Pop();
            }
        }
        finally
        {
            e.Dispose();
            while (stack.Count != 0) stack.Pop().Dispose();
        }
    }
}

以及您的特定問題的解決方案:

var orderedModel = unorderedModel.Where(item => item.Parent == null).OrderBy(item => item.SortOrder)
    .Expand(item => item.Children != null && item.Children.Any() ? item.Children.OrderBy(child => child.Title) : null)
    .ToList();

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM