Reputation: 38164
I have a collection of items. The one item can have another item, and another item can have another item. So on.
I do not know how many levels of nested items can have item. The level of nested items can be defined at run-time.
class Person
{
Person person;
public Person(Person _nestedPerson)
{
person = _nestedPerson;
}
public bool IsSelectedPerson { get; set; }
public string Name { get; set; }
}
and how items(Person
) can be nested:
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});
}
My goal is to take ALL objects(without duplicates) of persons(Person
) who IsSelectedPerson=true
?
I've played with Select()
var ee = list.Select(x=>x.IsSelectedFacet==true);//comparison should be done here
but it is not what I want, it just takes bool
values.
Update:
My expected result should be have one object of Person
with unique name. No matter how many there are objects with the same name. I would like to take just one object. Sorry for misleading. It should be look like this:
Upvotes: 0
Views: 2077
Reputation: 45967
Here is another approach to yield your list of items:
IEnumerable<Person> GetIsSelectedPerson(Person p)
{
Person temp = p;
while (temp != null)
{
if (temp.IsSelectedPerson)
{
yield return temp;
}
temp = temp.person;
}
}
Usage:
IEnumerable<Person> Result = GetIsSelectedPerson(rootPerson)
Upvotes: 3
Reputation: 5488
You can make a helper method to unwrap all nested objects
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;
}
Or if Person
class has only one nested object (Person person;
) you can use a yield
construction instead of the recursion
static IEnumerable<Person> UnwrapPerson(Person p)
{
yield return p;
while (p.person != null)
{
p = p.person;
yield return p;
}
}
In order to remove all duplicate persons, for example with the same name, you should implement IEqualityComparer<Person>
and then use Distinct
method.
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;
}
}
So final query should be similar to:
list.SelectMany(p => UnwrapPerson(p))
.Where(x => x.IsSelectedPerson == true)
.Distinct(new Comparer())
Upvotes: 4
Reputation: 35716
Do this to flatten the people,
Func<Person, IEnumerable<Person>> flattener = null;
flattener = p => new[] { p }
.Concat(
p.person == null
? Enumerable.Empty<Person>()
: (new [] { p.Person }).SelectMany(child => flattener(child)));
So you can do this,
flattener(person).Where(p => p.IsSelectedPerson);
Following you comments, what you possibly want is,
flattener(person)
.Where(p => p.IsSelectedPerson)
.Select(p => p.Name)
.Distinct();
Upvotes: 1
Reputation: 1339
Since you don't know the level of persons in the chain, the best is to use recursion. Two simple solutions (suppose you add the methods on Person class)
Create a method that receives a list, so you can fill it in the recursive call: List completeList = new List(); list[0].GetCompleteList(completeList); list[1].GetCompleteList(completeList);
public void GetCompleteList(List<Person> personsList)
{
personsList.Add(this);
if (person != null)
{
person.GetCompleteList(personsList);
}
}
The same, without parameter
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;
}
Upvotes: 1
Reputation: 14498
I would use some kind of visiting pattern with recursion to visit all the nested 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();
}
}
Upvotes: 2