我有一个奇怪的排序案例,我正在努力使用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大师可以帮助我......
答案 0 :(得分:3)
那么,您希望获得哪种数据类型?目前,它将是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
,这可能会给您完全相同的返回类型,但分组只会通过最顶层的类别。希望这实际上是你想要的......
答案 1 :(得分:1)
如果你有一棵树要走,你已经有了按类别分组的物品。你是否控制了视图的界面?
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
答案 2 :(得分:0)
看看这个blog。我喜欢ByHierarchy扩展方法
答案 3 :(得分:0)
这就是我解决问题的方法:
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>
我很想整合这个标签汤,但我现在必须要这样做。
答案 4 :(得分:0)
我感觉org.ccil.cowan.tagsoup从td标签中删除了样式属性。例如... Bill应用commandline.process后来自NetFlow Analyzer它只返回Bill From NetFlow Analyzer。