如何从嵌套类list<;T>;

本文关键字:lt gt list 嵌套 | 更新日期: 2023-09-27 18:20:04

我有一个问题使用这些相同的例子-这个问题集中在另一个问题上。给定以下类别:

   [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列表?

如何从嵌套类list<;T>;

据我所知,实现这一点的最简单方法是使用助手。

    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);
}
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);
}

SelectMany只会使层次结构的一个级别变平:

public IEnumerable<Person> FindLevel2 (Func<Person, bool> predicate)
{
  return family.Person.SelectMany(p => p.People).Where(predicate);
}

实际上,您可能想要层次结构的任意深度漫游。这最好通过递归(Untested)来完成。

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;
    }
  }
}

您不需要SelectManyyield 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)
}

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);
}