2015-10-11 73 views
2

我有如下記錄LINQ的排序與一捻

enter image description here

最後2記錄3和4的孩子,我希望能夠通過量的記錄進行排序,但它應該是該利息(父母)的人進行排序第一,然後他們的孩子應該經過這麼例如,它會是這樣

2000 
2000 
20001 
99.84 (child of the above) 
50000 
249.58 (child of the above) 

顯示基本上,我想我的排序量無視一個無線「IsInterest」被設置爲true,但是讓他們在父母之後出現。

我可以通過首先將所有父母帶入一個新的集合中,然後通過父母來查看是否有任何孩子,然後將父母插入到新集合中,但是我覺得這樣做效率不高髒碼,所以我想我會問,也許有人知道黑魔法。 這種排序還應該知道數量的asc/desc。

我可以發佈我的代碼拆分集合,並將它放在一起,如果它有幫助,但我想盡可能不使用該代碼。

我的排序方法接受一個字符串爲「升序」或「降序」如果這能幫助

謝謝

UPDATE2 我會指出,只有永遠將是2級,並且孩子將永遠只有一個父母(沒有祖父母),並且每個父母最多有一個孩子

更新代碼按照要求(字段名稱可能不同於數據庫字段..)

switch (sortMember.ToUpper()) 
      { 
       case "AMOUNT": 
        { 
         //check to see if any imputed interests exist 
         if (contributions.Any(x => x.IsImputedInterest)) 
         { 
          var children = contributions.Where(x => x.IsImputedInterest); 
          var sortedColl = contributions.Where(x => x.IsImputedInterest == false).OrderByWithDirection(x => x.ContributionAmount, sortDirection.ToUpper() == "DESCENDING").ToList(); 
          foreach (var child in children) 
          { 
           //find the parent 
           var parentIndex = sortedColl.FindIndex(x => x.ContributionId == child.ParentContirbutionId); 
           sortedColl.Insert(parentIndex+1, child); 

          } 

         } 
         else 
         { 
          contributions = contributions.OrderByWithDirection(x => x.ContributionAmount, sortDirection.ToUpper() == "DESCENDING"); 
         } 

         break; 
        } 
      } 

.................

public static IOrderedEnumerable<TSource> OrderByWithDirection<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, bool descending) 
    { 
     return descending ? source.OrderByDescending(keySelector) 
          : source.OrderBy(keySelector); 
    } 

    public static IOrderedQueryable<TSource> OrderByWithDirection<TSource, TKey>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector, bool descending) 
    { 
     return descending ? source.OrderByDescending(keySelector) 
          : source.OrderBy(keySelector); 
    } 
+2

向我們展示您當前的排序方法。 –

+0

您可以在'OrderBy' /'OrderByDescending'後面使用'ThenBy' /'ThenByDescending'。 – varocarbas

+0

你將如何做到這一點,它會這樣排序他們第一次,99.83,249.58,2000 .....這不是我想要的 – Zoinky

回答

2

這裏有一條語句的Linq的解決方案:

var desc = order == "descending"; 
var result = list 
    //group parents with it's children 
    .GroupBy(x => x.ParentId ?? x.Id) 
    //move the parent to the first position in each group 
    .Select(g => g.OrderBy(x => x.ParentId.HasValue).ThenBy(x => desc ? -x.Amount : x.Amount)) 
    //sort the groups by parents' amounts 
    .OrderBy(g => desc ? -g.First().Amount : g.First().Amount) 
    //retrieve the items from each group 
    .SelectMany(g => g); 

一些性能提示:

  • 如果總是最多隻有一個孩子或你,那麼您可以刪除ThenBy(...)不關心孩子訂購
  • 使用的if語句來檢查訂單並有聲明的兩個版本 - 使用OrderByDescending/ThenByDescending第二個,落三元運算符(desc ? ... : ...) - 否則它會爲每個評估item

我沒有給出與您當前的解決方案有關的性能保證 - 它可能會變慢。

+0

在主線程中更新,只會有一個孩子,並且只有一代(只有父母有一個孩子)沒有涉及祖父母 – Zoinky

1

您可以使用下面的泛型方法(不通過水平或父母的數量限制/小孩):

public static class Extensions 
{ 
    public static IEnumerable<T> ThenByHierarchy<T, TKey>(this IOrderedEnumerable<T> source, Func<T, TKey> keySelector, Func<T, TKey> parentKeySelector) 
    { 
     var itemByKey = source.ToDictionary(keySelector); 
     var processSet = new HashSet<T>(); 
     var stack = new Stack<T>(); 
     foreach (var item in itemByKey.Values) 
     { 
      for (var next = item; processSet.Add(next);) 
      { 
       stack.Push(next); 
       if (!itemByKey.TryGetValue(parentKeySelector(next), out next)) break; 
      } 
      while (stack.Count != 0) 
       yield return stack.Pop(); 
     } 
    } 
} 

只需在OrderBy序列的這樣

var result = contributions 
    .OrderByWithDirection(x => x.ContributionAmount, sortDirection.ToUpper() == "DESCENDING") 
    .ThenByHierarchy(x => x.ContributionId, x => x.ParentContirbutionId); 

末追加它事實證明,它並不那麼簡單。儘管上面的方法爲葉子元素以及元素與父元素之間的順序提供了正確的順序,但它並沒有正確排序父項。正確的是如下(使用從這裏How to flatten tree via LINQ?另一種可重複使用的方法,所以如果我們不指望它是不是真的比以前大得多):

public static class Extensions 
{ 
    public static IEnumerable<T> HierarchicalOrder<T, TKey>(this IEnumerable<T> source, Func<T, TKey> keySelector, Func<T, TKey> parentKeySelector, Func<IEnumerable<T>, IOrderedEnumerable<T>> order) 
    { 
     // Collect parent/child relation info 
     var itemById = source.ToDictionary(keySelector); 
     var childListById = new Dictionary<TKey, List<T>>(); 
     var rootList = new List<T>(); 
     foreach (var item in itemById.Values) 
     { 
      var parentKey = parentKeySelector(item); 
      List<T> childList; 
      if (parentKey == null || !itemById.ContainsKey(parentKey)) 
       childList = rootList; 
      else if (!childListById.TryGetValue(parentKey, out childList)) 
       childListById.Add(parentKey, childList = new List<T>()); 
      childList.Add(item); 
     } 
     // Traverse the tree using in-order DFT and applying the sort on each sublist 
     return order(rootList).Expand(item => 
     { 
      List<T> childList; 
      return childListById.TryGetValue(keySelector(item), out childList) ? order(childList) : null; 
     }); 
    } 
    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 result = contributions 
    .HierarchicalOrder(x => x.ContributionId, x => x.ParentContirbutionId, c => 
    .OrderByWithDirection(x => x.ContributionAmount, sortDirection.ToUpper() == "DESCENDING"));