如何从集合中获取所有嵌套项?

时间:2016-03-17 10:20:35

标签: c# linq

我有一系列物品。一个项目可以有另一个项目,另一个项目可以有另一个项目。等等。

我不知道嵌套项目可以有多少级别的项目。嵌套项的级别可以在运行时定义。

class Person
{
    Person person;
    public Person(Person _nestedPerson)
    {
        person = _nestedPerson;
    }

    public bool IsSelectedPerson { get; set; }
    public string Name { get; set; }
}

以及如何嵌套项目(Person):

IList<Person> list = new List<Person>();            
for (int startIndex = 0; startIndex < 5; startIndex++)
{
   list.Add(new Person(new Person(new Person(new Person(null) { Name="Bill", 
        IsSelectedPerson=true})) { Name = "Jessy", IsSelectedPerson = false }) 
        { Name = "Bond", IsSelectedPerson =true});//3 nested persons
   list.Add(new Person(new Person(null) { Name = "Kendell", 
        IsSelectedPerson = true }) { Name="Rosy", IsSelectedPerson=true});//2 nested persons
   //The next time it can be just one person without nested item(person). I do not know how many items(persons) will be nested
   //list.Add(new Person(null) { Name="Rosy", IsSelectedPerson=true});
}

我的目标是采取所有对象(没有重复Person的人IsSelectedPerson=true}

我玩过Select()

var ee = list.Select(x=>x.IsSelectedFacet==true);//comparison should be done here

但它不是我想要的,只需要bool个值。

更新

我的预期结果应该是Person一个具有唯一名称的对象。无论有多少具有相同名称的对象。我想只拿一个对象。很抱歉误导。它应该是这样的:

enter image description here

5 个答案:

答案 0 :(得分:4)

您可以创建一个帮助方法来解包所有嵌套对象

    IEnumerable<Person> UnwrapPerson(Person p)
    {
        List<Person> list = new List<Person>();
        list.Add(p);
        if (p.person != null)
            list.AddRange(UnwrapPerson(p.person));

        return list;
    }

或者,如果Person类只有一个嵌套对象(Person person;),则可以使用yield构造而不是递归

    static IEnumerable<Person> UnwrapPerson(Person p)
    {
        yield return p;
        while (p.person != null)
        {
            p = p.person;
            yield return p;
        }
    }

为了删除所有重复的人,例如使用相同的名称,您应该实施IEqualityComparer<Person>,然后使用Distinct方法。

class Comparer : IEqualityComparer<Person>
{
    public bool Equals(Person x, Person y)
    {
        return string.Equals(x.Name, y.Name);
    }

    public int GetHashCode(Person obj)
    {
        string name = obj.Name;
        int hash = 7;
        for (int i = 0; i < name.Length; i++)
        {
            hash = hash * 31 + name[i];
        }

        return hash;
    }
}

所以最终查询应该类似于:

 list.SelectMany(p => UnwrapPerson(p))
     .Where(x => x.IsSelectedPerson == true)
     .Distinct(new Comparer())

答案 1 :(得分:3)

这是另一种产生项目列表的方法:

IEnumerable<Person> GetIsSelectedPerson(Person p)
{
    Person temp = p;
    while (temp != null)
    {
        if (temp.IsSelectedPerson)
        {
            yield return temp;
        }
        temp = temp.person;
    }           
}

<强>用法:

IEnumberable<Person> Result = GetIsSelectedPerson(rootPerson)

答案 2 :(得分:1)

我会使用某种访问模式和递归来访问所有嵌套的Persons

class Person
{
   public static List<Person> selectedPersons;
   Person person;
   public Person(Person _nestedPerson)
   {
       if(selectedPersons == null)
         selectedPersons = new List<Person>();
       person = _nestedPerson;
   }

   public bool IsSelectedPerson { get; set; }
   public string Name { get; set; }

   public void Visit()
   {
       if(this.IsSelectedPerson)
         selectedPersons.Add(this);
       if(this.person != null)
         this.person.Visit();
   }
}

答案 3 :(得分:1)

这样做可以使人们变得平坦,

Func<Person, IEnumerable<Person>> flattener = null;
flattener = p => new[] { p }
    .Concat(
        p.person == null 
            ? Enumerable.Empty<Person>()
            : (new [] { p.Person }).SelectMany(child => flattener(child)));

所以你可以这样做,

flattener(person).Where(p => p.IsSelectedPerson);

在您发表评论后,您可能想要的是,

flattener(person)
   .Where(p => p.IsSelectedPerson)
   .Select(p => p.Name)
   .Distinct();

答案 4 :(得分:1)

由于您不知道链中的人员级别,最好是使用递归。两个简单的解决方案(假设您在Person类上添加方法)

  1. 创建一个接收列表的方法,以便在递归调用中填写它:     列表completeList = new List();     列表[0] .GetCompleteList(completeList);     列表[1] .GetCompleteList(completeList);

    public void GetCompleteList(List<Person> personsList)
    {
        personsList.Add(this);
        if (person != null)
        {
            person.GetCompleteList(personsList);
        }
    }
    
  2. 相同,没有参数

    List<Person> completeList = new List<Person>();
    completeList.AddRange(list[0].GetCompleteList());
    completeList.AddRange(list[1].GetCompleteList());
    
    // Another way: with linq
    var myPersons  list.SelectMany(m => m.GetCompleteList());
    
    public List<Person> GetCompleteList()
     {
         List<Person> returnList = new List<Person>();
         returnList.Add(this);
         if (person != null)
         {
             returnList.AddRange(person.GetCompleteList());
         }
         return returnList;
     }