将层次结构展平为专门排序的列表
Flatten a hierarchy as specifically sorted list
我有一个简单的人员层次结构 (EntityFramework Codefirst)。
public class Person
{
[Key]
public int Id { get; set; }
public int? ParentId { get; set; }
[Required]
public string Name { get; set; }
// Navigation
[ForeignKey("ParentId")]
public virtual Person Parent { get; set; }
public virtual ICollection<Person> Children { get; set; }
}
我的目标:
我需要一种创建 List<Person>
的方法,它 (1) 按 parent 聚类并且 (2) 在每个级别 按名称字母顺序排序 :
示例输出:
- 阿尔弗雷德
- 本(阿尔弗雷德的儿子)
- 玛丽恩(阿尔弗雷德的女儿)
- 拉尔夫(阿尔弗雷德之子)
- 克里斯汀
- Ally(克里斯汀的女儿)
- 戴夫
- 多丽丝
- 詹姆斯(多丽丝的儿子)
- 克拉斯(多丽丝的儿子)
Alfred、Christine、Dave 和 Doris 都是 parent。它们按字母顺序排序。他们所有人都有 children(Dave 除外)。 children 也按字母顺序排序(在列表中它们显示为 "grouped" 和 parent)。 "son of" 和 "daughter of" 只是给你们的评论(结果列表中不需要这个)。
我可以轻松地创建这种带有嵌套循环的列表。但我相信这不会非常有效或优雅。但也许我错了。
你们中有人有好的 idea/suggestion(也许是 LINQ)吗?谢谢!!
哦,我应该提一下,层次结构只有 2 层。所以,只有 parents 和 children(但没有孙子)。
这行得通吗?
List<Person> sortPeople(List<Person> people)
{
List<Person> result = new List<Person>();
foreach (Person p in people.OrderBy(_ => _.Name).ToList())
{
result.Add(p);
result.AddRange(p.Children.OrderBy(_ => _.Name).ToList());
}
return result;
}
这只有在有两个级别时才有效,我不知道这如何转化为 SQL:
var parents = people.Select(person=>person.Parent == null);
var parentsWithChildrenGrouped =
parents
.SelectMany(parent=>parent.Children.Select(child=>new {Parent = parent, Child = child, Out = child}))
.Union(parents.Select(parent => new {Parent = parent, Child = null, Out = parent}))
.OrderBy(x=>x.Parent).ThenBy(x=>x.Child)
.Select(x=>x.Out);
此查询首先创建列表,其中每个项目都有 child 和 parent。然后它首先根据 parent 排序,然后根据 child 排序。然后是第三个 "out" 属性 定义该行的用途。
我知道这可以很容易地转换为有效 SQL,但我不确定 EF 将如何处理它。
var sortedPeople =
from p in people
orderby (p.Parent == null ? p.Name : p.Parent.Name),
p.Parent == null ? 1 : 2, // this ensures parents appear before children
p.Name
select p;
我有一个简单的人员层次结构 (EntityFramework Codefirst)。
public class Person
{
[Key]
public int Id { get; set; }
public int? ParentId { get; set; }
[Required]
public string Name { get; set; }
// Navigation
[ForeignKey("ParentId")]
public virtual Person Parent { get; set; }
public virtual ICollection<Person> Children { get; set; }
}
我的目标:
我需要一种创建 List<Person>
的方法,它 (1) 按 parent 聚类并且 (2) 在每个级别 按名称字母顺序排序 :
示例输出:
- 阿尔弗雷德
- 本(阿尔弗雷德的儿子)
- 玛丽恩(阿尔弗雷德的女儿)
- 拉尔夫(阿尔弗雷德之子)
- 克里斯汀
- Ally(克里斯汀的女儿)
- 戴夫
- 多丽丝
- 詹姆斯(多丽丝的儿子)
- 克拉斯(多丽丝的儿子)
Alfred、Christine、Dave 和 Doris 都是 parent。它们按字母顺序排序。他们所有人都有 children(Dave 除外)。 children 也按字母顺序排序(在列表中它们显示为 "grouped" 和 parent)。 "son of" 和 "daughter of" 只是给你们的评论(结果列表中不需要这个)。
我可以轻松地创建这种带有嵌套循环的列表。但我相信这不会非常有效或优雅。但也许我错了。
你们中有人有好的 idea/suggestion(也许是 LINQ)吗?谢谢!!
哦,我应该提一下,层次结构只有 2 层。所以,只有 parents 和 children(但没有孙子)。
这行得通吗?
List<Person> sortPeople(List<Person> people)
{
List<Person> result = new List<Person>();
foreach (Person p in people.OrderBy(_ => _.Name).ToList())
{
result.Add(p);
result.AddRange(p.Children.OrderBy(_ => _.Name).ToList());
}
return result;
}
这只有在有两个级别时才有效,我不知道这如何转化为 SQL:
var parents = people.Select(person=>person.Parent == null);
var parentsWithChildrenGrouped =
parents
.SelectMany(parent=>parent.Children.Select(child=>new {Parent = parent, Child = child, Out = child}))
.Union(parents.Select(parent => new {Parent = parent, Child = null, Out = parent}))
.OrderBy(x=>x.Parent).ThenBy(x=>x.Child)
.Select(x=>x.Out);
此查询首先创建列表,其中每个项目都有 child 和 parent。然后它首先根据 parent 排序,然后根据 child 排序。然后是第三个 "out" 属性 定义该行的用途。
我知道这可以很容易地转换为有效 SQL,但我不确定 EF 将如何处理它。
var sortedPeople =
from p in people
orderby (p.Parent == null ? p.Name : p.Parent.Name),
p.Parent == null ? 1 : 2, // this ensures parents appear before children
p.Name
select p;