簡體   English   中英

使用List(T).GroupBy()對嵌套對象進行分組

[英]Grouping nested objects using List(T).GroupBy()

我有一個奇怪的排序案例,我正在努力使用LINQs GroupBy方法。

我有兩個類:Category和Item。 每個項目都有一個類別,一個類別可以有一個父類別。 我需要做的是按照正確的類別組織所有項目,但是如果有的話,我還想按父類別對類別進行排序。

理想情況下,我應該能夠將結果可視化為:

<Category 1> 
  <Item 1>  
  <Item 2> 
</Category 1> 
<Category 2>
  <Category 3>  
    <Item 3>  
    <Item 4>  
  </Category 3>
</Category 2>  
<Category 4>  
  <Item 5>
</Category 4>
<Category 5>  
  <Item 6>
</Category 5>  

我目前正在使用items.GroupBy(x => x.Category) ,它為我提供除父類別之外的所有內容。 所以我的結果如下:

<Category 1> 
    <Item 1>  
    <Item 2> 
</Category 1> 

    <Category 3>  
      <Item 3>  
      <Item 4>  
    </Category 3>

<Category 4>  
  <Item 5>
</Category 4> 
<Category 5>  
  <Item 6>
</Category 5>

問題是(在此示例中)未列出類別3(類別2)的父類別。

我開始在嵌套組中閑逛,但在考慮自己手動走樹之前我沒有走得太遠(foreach'ing)。 在我這樣做之前,我希望這里的LINQ大師可以幫助我...

那么,您期望獲得什么樣的數據類型? 目前,它將是IGrouping<Category, Item>但是如果您希望最頂層的類別是關鍵字,則值可能是項目類別。

您已將結果顯示為XML,但您實際上將如何使用它們? 鑒於您已經獲得的結果,您不能輕易獲得父類別嗎? 您是否需要在實際分組部分中使用父類別? 如果兩個類別具有相同的父類別,您是否希望將該父類別中的所有項目混合在一起?

對不起所有問題 - 但我們知道的越多,我們就能越好地幫助您。

編輯:如果您只想按最高類別對項目進行分組,則可以執行此操作

items.GroupBy(x => GetTopmostCategory(x))
...
public Category GetTopmostCategory(Item item)
{
    Category category = item.Category;
    while (category.Parent != null)
    {
        category = category.Parent;
    }
    return category;
}

(你可以將它放入CategoryItem 。)這會給你完全相同的返回類型,但是分組只能通過最頂層的類別。 希望這實際上是你想要的......

如果你有一棵樹要走,你已經有了按類別分組的物品。 你控制視圖的界面了嗎?

public abstract class TreeNode {
  private readonly int name;
  private Category c = null;

  public int Name { get { return name; } }
  public Category Parent { get { return c; } }
  public abstract string Tag { get; }

  public TreeNode(int n, Category c) {
    this.name = n;
    AssignCategory(c);
  }

  public void AssignCategory(Category c) {
    if (c != null) {
      this.c = c;
      c.AddChild(this);
    }
  }

  public virtual IList<TreeNode> Children {
    get { return null; }
  }
}

項目和類別看起來像

public class Item : TreeNode {
  public Item(int n, Category c) : base(n, c) {}

  public override string Tag { get { return "Item"; } }
}

public class Category : TreeNode {
  List<TreeNode> kids = new List<TreeNode>();

  public Category(int n, Category c) : base(n, c) {}

  public void AddChild(TreeNode child) {
    kids.Add(child);
  }

  public override string Tag { get { return "Category"; } }

  public override IList<TreeNode> Children {
    get { return kids; }
  }
}

然后你可以用一個陳舊的控制台顯示器來顯示它們:

public class CornyTextView {
  public int NodeDepth(TreeNode n) {
    if (n.Parent == null)
      return 0;
    else
      return 1 + NodeDepth(n.Parent);
  }

  public void Display(IEnumerable<TreeNode> nodes) {
    foreach (var n in nodes.OrderBy(n => n.Name)) {
      for (int i = 0; i < NodeDepth(n); i++)
        Console.Write("  ");

      Console.WriteLine("- " + n.Tag + " " + n.Name.ToString());
      if (n.Children != null)
        Display(n.Children);
    }
  }
}

所以為你的例子生成輸出:

public static void Main() {
  var cats = new [] {
    new Category(1, null),
    new Category(2, null),
    new Category(3, null),
    new Category(4, null),
    new Category(5, null),
  };
  cats[2].AssignCategory(cats[1]);

  var items = new[] {
    new Item(6, cats[4]),
    new Item(5, cats[3]),
    new Item(3, cats[2]), new Item(4, cats[2]),
    new Item(1, cats[0]), new Item(2, cats[0]),
  };

  new CornyTextView()
        .Display(cats.Where(c => c.Parent == null)
                     .Select(c => c as TreeNode));
}

請注意,即使items被洗牌,輸出也是如此

- Category 1
  - Item 1
  - Item 2
- Category 2
  - Category 3
    - Item 3
    - Item 4
- Category 4
  - Item 5
- Category 5
  - Item 6

看看這個博客 我喜歡ByHierarchy擴展方法

這就是我解決問題的方法:

var items = Items.GroupBy(x => x.Category).GroupBy(y => y.Key.Parent);

這讓我做了以下(令人討厭的)渲染標記:

<table>
    <tr>
        <th>Item Description</th>
        <th>Value</th>
    </tr>
    <% foreach (var parentGroup in Model.Items) { %>
        <% if (parentGroup.Key != null) { %>

        <tr>
            <th colspan="2" style="background-color:#ff9900;"><%= parentGroup.Key.Label %></th>
        </tr>

            <% foreach (var childGroup in parentGroup) { %>
                <tr>
                    <th colspan="2"><%= childGroup.Key.Label %></th>
                </tr>
                <% foreach (var item in childGroup) { %>
                    <tr>
                        <td><%= item.Description %></td>
                        <td><%= String.Format("{0:c}", item.Value) %></td>
                    </tr>
                <% } %>
            <% } %>
        <% } else { %>
            <% foreach (var childGroup in parentGroup) { %>
                <tr>
                    <th colspan="2" style="background-color:#ff9900;"><%= childGroup.Key.Label %></th>
                </tr>
                <% foreach (var item in childGroup) { %>
                    <tr>
                        <td><%= item.Description %></td>
                        <td><%= String.Format("{0:c}", item.Value) %></td>
                    </tr>
                <% } %>
            <% } %>

        <% } %>
    <% } %>
</table>

我很想鞏固這個標簽湯,但我現在必須要用它。

我有一種感覺,org.ccil.cowan.tagsoup從td標簽中刪除了樣式屬性。 例如... Bill應用commandline.process后來自NetFlow Analyzer它只返回Bill From NetFlow Analyzer。

暫無
暫無

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

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