LINQ to Get All heirerichal children - c#

I have been digging this quite a while.
public class Person
{
public string Name { get; set; }
public string Age { get; set; }
public List<Person> Children { get; set; }
}
I want a single LINQ query to find out "All the persons whose Age > 4 in this collection".
Note: You have to traverse Collection of Person + Collection of Children, so each children object will have a collection of Person till Children becomes null.

First i can't understand why all your properties private and Age is not int type. So my class looks like this:
public partial class Person
{
public string Name { get; set; }
public int Age { get; set; }
public List<Person> Childrens { get; set; }
}
Note partial word. This word will allow you to place your class logic in separate files and this could be usefull when you creating some custom logic in class.
Then I simply create this method in different file:
public partial class Person
{
public Person GetPersonWithChindren(int maxAge)
{
return new Person
{
Age = this.Age,
Name = this.Name,
Childrens = this.Childrens != null
? this.Childrens
.Where(x => x.Age < maxAge)
.Select(x => x.GetPersonWithChindren(maxAge)) //this line do recursive magic
.ToList()
: null
};
}
}
As you can see this method checking Age of each child and if Age is ok then it checks next level of hierarchy untill Childrens is null.
So you can use it like this:
var person = new Person()
{
//initialisation of your collection here
}
//result will contains only nodes where Person have age < 4 and Childs that have age < 4
var result = person.GetPersonWithChindren(4);
Note that this solution will work normal with linqToEntities. But if you using LinqToSQL this expression produces query to DB on each Person entity. So if you have many Persons and deep hierarhy it will costs you a lot of machine time. In that case you should to write stored procedure with CTE instead of LinQ query.
UPDATE:
You even can write more general solution with a help of Func<T> class like this:
public partial class Person
{
public Person GetPersonWithChindren(Func<Person, bool> func)
{
return new Person
{
Age = this.Age,
Name = this.Name,
Childrens = this.Childrens != null
? this.Childrens
.Where(x => func(x))
.Select(x => x.GetPersonWithChindren(func))
.ToList()
: null
};
}
}
And then you can use it like this:
var result = person.GetPersonWithChindren(x => x.Age < 4);
You always can change your criteria now where you want to use your function.

Create a visitor. In this example by implementing a helper class:
public static class Helpers
public static IEnumerable<Person> GetDescendants(this Person person)
{
foreach (var child in person.Children)
{
yield return child;
foreach (var descendant in child.GetDescendants())
{
yield return descendant;
}
}
}
this is one of the times where the "yield return many" would be useful.

If you're ensuring that .Children is automatically created, then this works:
Func<Person, Func<Person, bool>, Person> clone = null;
clone = (p, f) => f(p) ? new Person()
{
Name = p.Name,
Age = p.Age,
Children = p.Children.Select(c => clone(c, f)).Where(x => x != null).ToList(),
} : null;
var olderThan4 = clone(person, p => p.Age > 4);
Yes, that's it. Effectively three lines.
If you start with this data:
var person = new Person()
{
Name = "Fred", Age = 30,
Children = new List<Person>()
{
new Person() { Name = "Bob", Age = 7, },
new Person() { Name = "Sally", Age = 3, }
},
};
...then you get this result:
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
private List<Person> _children = null;
public List<Person> Children
{
get
{
if (_children == null)
{
_children = new List<Person>();
}
return _children;
}
set
{
_children = value;
}
}
}

Related

Order list by parent and child and parent of the child

I'm trying to order List that should look like this
Parent
Child1 (simultaneously children and parent)
Child2 (Children of Child1)
Child3
In using Class that contain information's about ID, ParentID and etc.
I'm trying to make this work using LINQ and tried different solution but no one work completely, I know that with recursively function will work (but really don't like that), can someone help me to make working with LINQ ?
i tried this code but Child2 don't appearing.
List<Person> orderedList = new List<Person>();
persons.ForEach(x => {
if (x.ParentID == 0) {
orderedList.Add(x);
orderedList.AddRange(persons.Where(child => child.ParentID == x.Id));
}
});
For those who are voting "negative" remember no one was god at programming at the beginning, if i come here that means that I'm struggle to fix the problem for x hours. And also if you think that my English is bad i know that already, I'm not born to speak English perfectly but those who wants to help will help. :)
Whole Code
public class Person{
public int Id { get; set; }
public string MenuName { get; set; }
public int? ParentID { get; set; }
public string isHidden { get; set; }
public string LinkURL { get; set; }
}
public static List<Person> AddPersons(){
var persons = new List<Person>();
using (var reader = new StreamReader(#"C:\Users\AceDuk\Desktop\Navigation.csv")){
var line = reader.ReadLine(); //Da se izbegne headerot
while ((line = reader.ReadLine()) != null){
var values = line.Split(';');
if (values[2] == "NULL") {
values[2] = "0";
}
persons.Add(new Person(){
Id = Int32.Parse(values[0]),
MenuName = values[1],
ParentID = Int32.Parse(values[2]),
isHidden = values[3],
LinkURL = values[4]
});
}
}
persons.RemoveAll(x => x.isHidden == "True"); //Izbrisi gi site sto se hidden ne gi pokazuvaj..
//persons = persons.OrderBy(x => x.MenuName).ToList(); //Ordered
persons = persons.OrderBy(x => x.LinkURL).ToList(); //Ordered
return persons;
}
static void Main(string[] args){
List<Person> persons = AddPersons();
List<Person> orderedList = new List<Person>();
persons.ForEach(x => {
if (x.ParentID == 0) {
orderedList.Add(x);
orderedList.AddRange(persons.Where(child => child.ParentID == x.Id));
}
});
foreach (var item in orderedList) {
Console.WriteLine(item.MenuName);
}
}
Create a double-ended queue (deque) data structure by extending a linked list:
public class Deque<T> : LinkedList<T> {
public void Enqueue(T item) => AddLast(item);
public T Dequeue() {
var item = First.Value;
RemoveFirst();
return item;
}
public void EnqueueRange(IEnumerable<T> items) {
foreach (var item in items)
Enqueue(item);
}
public void Push(T item) => AddFirst(item);
public T Pop() => Dequeue();
public void PushRange(IEnumerable<T> items) {
foreach (var item in items)
Push(item);
}
public T Peek() => Last.Value;
}
Now, create a mapping from Id to children using ToLookup:
var childrenDictionary = persons.Where(p => p.ParentID != 0).ToLookup(p => p.ParentID);
Finally, use the deque to create a working list and add all the root nodes:
var workDeque = new Deque<Person>();
workDeque.EnqueueRange(persons.Where(p => p.ParentID == 0));
Now you can go through the workDeque, adding each root node to the orderedPersons and then pushing the children of the node onto workDeque to be worked next:
var orderedPersons = new List<Person>();
while (workDeque.Count > 0) {
var nextPerson = workDeque.Dequeue();
orderedPersons.Add(nextPerson);
workDeque.PushRange(childrenDictionary[nextPerson.Id]);
}

How to add elements to IList collection using a function

I'm just trying to understand an example of WPF treeview.
My goal ist to populate a treeview with some items, stored in a List.
This is the example:
public class Person
{
readonly List<Person> _children = new List<Person>();
public IList<Person> Children
{
get { return _children; }
}
public string Name { get; set; }
}
//Some added nodes:
public static Person GetFamilyTree()
{
// In a real app this method would access a database.
return new Person
{
Name = "David Weatherbeam",
Children =
{
new Person
{
Name="Alberto Weatherbeam",
Children=
{
new Person
{
Name="Zena Hairmonger",
Children=
{
new Person
{
Name="Sarah Applifunk",
}
}
},
new Person
{
Name="Jenny van Machoqueen",
Children=
{
new Person
{
Name="Nick van Machoqueen",
},
new Person
{
Name="Matilda Porcupinicus",
},
new Person
{
Name="Bronco van Machoqueen",
}
}
}
}
}
}
};
}
So far it works.
Now I'd like to replace the static persons unter the first parent node with a list that I created before, reading a file.
XDocument ecad = XDocument.Load(openFileDialog.FileName);
GlobalVar.persons = new List<Persons>(); //globally available list
Person person = null;
//Einlesen der XML-Datei in lokale Variable (Model)
foreach(XElement elem in ecad.Descendants("Variable"))
{
if (elem.Element("Name") != null)
{
person = new Person(){ Name = elem.Element("Name").Value };
persons.Add(person);
}
}
Now I'd like to add these Persons to a root-person
//GlobalVar.List<Persons> persons
public static Person GetFamilyTree()
{
return new Person{
Name = "Family",
Children = persons
}
}
So how can I replace the Children = new... with the function that reads the data from file?
I'm really confused
What I think is confusing, Children is a readonly property but with the "static Children" it works, with the IList it doesn't (because of readonly). Can someone explain the difference, please?
Your GetFamilyTree() method uses a nested object initializer. Please refer to Jon Skeet's answer here for more information.
If you intend to read data from some source and then set the Children property, you should add a setter to it. You can also remove the backing field:
public class Person
{
public IList<Person> Children { get; set; } = new List<Person>();
public string Name { get; set; }
}

Build Dynamic query using Expression Tree

I am working on dynamic query building in LINQ using Expression Tree.
I have taken the reference to the following post
https://www.codeproject.com/Tips/582450/Build-Where-Clause-Dynamically-in-Linq
How can I build expression if I want to check all the element in the list contains in another collection or not?
I have a Person class
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
and I have a list
List<Person> personList = new List<Person>()
{
new Person{ Name = "Shekhar", Age = 31},
new Person{ Name = "Sandip", Age = 32},
new Person{ Name = "Pramod", Age = 32},
new Person{ Name = "Kunal", Age = 33}
};
I have another list
List<string> nameList = new List<string>() { "Sandip", "Prashant" };
How can I build expression tree to check all the element in the list "nameList" contains in "personList" and give result true or false?
try this:
public bool Find(List<string> nameList, List<Person> personList)
{
foreach (var name in nameList)
if (personList.FirstOrDefault(person => person.Name == name) != null)
{
// Find
return true;
}
return false;
}
try this:
bool contained = !personList.Select(l=>l.Name).Except(nameList).Any();

How can I get all nested items from a collection?

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

Cleanest Way To Map Entity To DTO With Linq Select?

I've been trying to come up with a clean and reusable way to map entities to their DTOs. Here is an example of what I've come up with and where I'm stuck.
Entities
public class Person
{
public int ID { get; set; }
public string Name { get; set; }
public Address Address { get; set; }
// Other properties not included in DTO
}
public class Address
{
public int ID { get; set; }
public string City { get; set; }
// Other properties not included in DTO
}
DTOs
public class PersonDTO
{
public int ID { get; set; }
public string Name { get; set; }
public AddressDTO Address { get; set; }
}
public class AddressDTO
{
public int ID { get; set; }
public string City { get; set; }
}
Expressions
This is how I began to handle the mapping. I wanted a solution that wouldn't execute the query before mapping. I've been told that if you pass a Func<in, out> instead of Expression<Func<in, out>> that it will execute the query before mapping.
public static Expressions
{
public static Expression<Func<Person, PersonDTO>> = (person) => new PersonDTO()
{
ID = person.ID,
Name = person.Name,
Address = new AddressDTO()
{
ID = person.Address.ID,
City = person.Address.City
}
}
}
One issue with this is that I already have an expression that maps an Address to an AddressDTO so I have duplicated code. This will also break if person.Address is null. This gets messy very quick especially if I want to display other entities related to person in this same DTO. It becomes a birds nest of nested mappings.
I've tried the following but Linq doesn't know how to handle it.
public static Expressions
{
public static Expression<Func<Person, PersonDTO>> = (person) => new PersonDTO()
{
ID = person.ID,
Name = person.Name,
Address = Convert(person.Address)
}
public static AddressDTO Convert(Address source)
{
if (source == null) return null;
return new AddressDTO()
{
ID = source.ID,
City = source.City
}
}
}
Are there any elegant solutions that I'm missing?
If you want to create mappings manually then you can use Select on the collection in the following way:
Some test data:
var persons = new List<Person>
{
new Person() {ID = 1, Name = "name1", Address = new Address() {ID = 1, City = "city1"}},
new Person() {ID = 2, Name = "name2", Address = new Address() {ID = 2, City = "city2"}},
new Person() {ID = 3, Name = "name3", Address = new Address() {ID = 1, City = "city1"}}
};
Mapping methods:
public static PersonDTO ToPersonDTOMap(Person person)
{
return new PersonDTO()
{
ID = person.ID,
Name = person.Name,
Address = ToAddressDTOMap(person.Address)
};
}
public static AddressDTO ToAddressDTOMap(Address address)
{
return new AddressDTO()
{
ID = address.ID,
City = address.City
};
}
Actual usage:
var personsDTO = persons.Select(x => ToPersonDTOMap(x)).ToList();
Keep in mind that if this was a real query is would not get executed as long as it was IQueryable, it would be executed once you materialize it (using ToList() for example).
However, I would consider using some framework which could do it (the mappings) for you automatically (if your mapping are as simple as provided example(.
Just use AutoMapper.
Example:
Mapper.CreateMap<Address, AddressDTO>();
Mapper.CreateMap<Person, PersonDTO>();
Your query will execute when the mapping is performed but if there are fields in the entity that you're not interested use Project().To<> which is available both for NHibernate and EntityFramework. It will effectively do a select on the fields specified in the mapping configurations.
Automapper is the best way .
For me, I use this for simple objects only, but I don't recommend it
public static class ObjectMapper
{
public static T Map<T>(object objfrom, T objto)
{
var ToProperties = objto.GetType().GetProperties();
var FromProperties = objfrom.GetType().GetProperties();
ToProperties.ToList().ForEach(o =>
{
var fromp = FromProperties.FirstOrDefault(x => x.Name == o.Name && x.PropertyType == o.PropertyType);
if (fromp != null)
{
o.SetValue(objto, fromp.GetValue(objfrom));
}
});
return objto;
}
}
And I call it like that wherever I want
var myDTO= ObjectMapper.Map(MyObject, new MyObjectDTO());
You could either use AutoMapper or write extension methods like these:
public static class PersonMapper
{
public static PersonDTO ConvertToDTO(this Person person)
{
return new PersonDTO { ID = person.ID, Name = person.Name, Address = person.Address.ConvertToDTO() };
}
public static IEnumerable<PersonDTO> ConvertToDTO(this IEnumerable<Person> people)
{
return people.Select(person => person.ConvertToDTO());
}
}
public static class AddressMapper
{
public static AddressDTO ConvertToDTO(this Address address)
{
return new AddressDTO { ID = address.ID, City = address.City };
}
public static IEnumerable<AddressDTO> ConvertToDTO(this IEnumerable<Address> addresses)
{
return addresses.Select(address => address.ConvertToDTO());
}
}
You could then map a Person object to a PersonDTO object like this:
public class Program
{
static void Main(string[] args)
{
Person person = new Person { ID = 1, Name = "John", Address = new Address { ID = 1, City = "New Jersey" } };
PersonDTO personDTO = person.ConvertToDTO();
Console.WriteLine(personDTO.Name);
}
}
I see the way you want to do it. I can propose you this solution:
public class PersonDTO
{
public int ID { get; set; }
public string Name { get; set; }
public AddressDTO Address { get; set; }
public static Expression<Func<Entities.Person, PersonDTO>> PersonSelector
{
get
{
return person => new PersonDTO()
{
ID = x.Id,
Name = x.Name,
Address = x.Address
.Select(AddressDTO.AddressSelector)
};
}
}
}
public async Task<PersonDTO> GetPerson(int id)
{
var person = await _personRepository.Get(id, PersonDTO.PersonSelector);
return person;
}
public async Task<TResult> Get<TResult>(int id, Expression<Func<Person, TResult>> selector)
{
var result = await _context.Persons
.Where(x => x.Id == id)
.Select(selector)
.SingleOrDefaultAsync();
return result;
}

Categories