[英]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;
}
(你可以將它放入Category
或Item
。)這會給你完全相同的返回類型,但是分組只能通過最頂層的類別。 希望這實際上是你想要的......
如果你有一棵樹要走,你已經有了按類別分組的物品。 你控制視圖的界面了嗎?
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.