如何从嵌套类List <T>获取展平列表?

IAb*_*act 7 c# linq-to-objects

我有一个使用这些相同例子的问题 - 这个问题集中在一个不同的问题上.鉴于以下课程:

   [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
   }
Run Code Online (Sandbox Code Playgroud)

如果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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我需要能够在平坦的列表中执行谓词Person.在上面的例子中,SelectMany并没有像我希望的那样压扁列表.以上实际上不会编译,因为无法确定推断类型.

如何让Family.Person集合成为一个扁平的Person列表?

arm*_*oon 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;
    }
Run Code Online (Sandbox Code Playgroud)

然后,您可以针对此列表运行谓词:

public IEnumerable<Person> Find (Func<Person, bool> predicate) {
    var familyRoot = new Person() { People = family.Person };
    return FlattenTree(familyRoot).Where(predicate);
}
Run Code Online (Sandbox Code Playgroud)

  • +1我喜欢它 - 也欢迎来到stackoverflow! (2认同)

Pet*_*ský 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);
}
Run Code Online (Sandbox Code Playgroud)

  • 我建议使用Concat over Union,因为不需要筛选交叉设置重复项. (2认同)