我有question using these same examples - 这个问题主要针对不同的问题。 鉴于以下类别:
[XmlRoot]
public class Family {
[XmlElement]
public List<Person> Person;
}
public class Person {
[XmlAttribute("member")]
public MemberType Member { get; set; }
[XmlAttribute("id")]
public int Id { get; set; }
[XmlElement]
public string Surname { get; set; }
[XmlElement]
public string Forename { get; set; }
[XmlElement("Person")]
public List<Person> People;
}
public enum MemberType {
Father,
Mother,
Son,
Daughter
}
如果Family
的方法定义如下:
public IEnumerable<Person> Find (Func<Person, bool> predicate) {
// how do I get SelectMany to flatten the list?
foreach (var p in family.Person.SelectMany(p => p)) {
if(predicate(p)) {
yield return p;
}
}
}
我需要能够在Person
的展平列表上执行谓词。在上面的例子中,SelectMany
并没有像我希望的那样压缩列表。以上实际上不会编译,因为无法确定推断类型。
如何让Family.Person集合成为一个扁平的Person列表?
答案 0 :(得分:6)
据我所知,实现这一目标的最简单方法是使用助手。
private List<Person> FlattenTree(Person person)
{
var accumulator = new List<Person>();
FlattenPersonHelper(person, accumulator);
return accumulator;
}
private void FlattenPersonHelper(Person person, List<Person> accumulator)
{
accumulator.Add(person);
foreach (var child in person.People)
{
FlattenPersonHelper(child, accumulator);
}
return;
}
然后,您可以针对此列表运行谓词:
public IEnumerable<Person> Find (Func<Person, bool> predicate) {
var familyRoot = new Person() { People = family.Person };
return FlattenTree(familyRoot).Where(predicate);
}
答案 1 :(得分:5)
public IEnumerable<Person> Find(IEnumerable<Person> input, Func<Person, bool> predicate) {
return input.Select(p =>
{
var thisLevel = new List<Person>();
if(predicate(p))
thisLevel.Add(p);
return thisLevel.Union(Find(p.People ?? new List<Person>(), predicate));
}
).SelectMany(p => p);
}
答案 2 :(得分:4)
SelectMany仅展平一个层次结构:
public IEnumerable<Person> FindLevel2 (Func<Person, bool> predicate)
{
return family.Person.SelectMany(p => p.People).Where(predicate);
}
您可能实际上想要层次结构的任意深度遍历。这最好通过递归(未经测试)完成。
public IEnumerable<Person> Find(Func<Person, bool> predicate)
{
foreach(Person p in family.Person)
{
IEnumerable<Person> result = FindFromPerson(p);
foreach(Person x in result)
{
yield return x;
}
}
}
public IEnumerable<Person> FindFromPerson(Person p, Func<Person, bool> predicate)
{
if predicate(p)
{
yield return p;
}
foreach(Person child in p.People)
{
IEnumerable<Person> childResults = FindFromPerson(child);
foreach(Person x in childResults)
{
yield return x;
}
}
}
答案 3 :(得分:3)
您不需要SelectMany
和yield return
- 您需要其中一个:
public IEnumerable<Person> Find (Func<Person, bool> predicate) {
foreach (var p in family.Person) {
if(predicate(p)) {
yield return p;
}
}
}
或强>
public IEnumerable<Person> Find (Func<Person, bool> predicate) {
return family.Person.Where(p => predicate(p)); // Can be written simply as Where(predicate)
}
答案 4 :(得分:2)
family.Person
已经一个展平列表;没有必要在其上调用SelectMany
。
foreach (var p in family.Person) {
if(predicate(p)) {
yield return p;
}
}
此外,你可以更简单地做:
public IEnumerable<Person> Find (Func<Person, bool> predicate) {
return family.Person.Where(predicate);
}