Group a List<Object> into List<List<Object>> - c#

How can I Group a List<Person> Persons by Person.Age using LINQ and create a new List<List<Person>> PersonsByAge ?
public class Person
{
public string Name;
public int Age
}

A GroupBy and a few ToList calls should do the job:
List<Person> persons = ...;
List<List<Person>> byAge = persons
.GroupBy(p => p.Age)
.Select(ps => ps.ToList())
.ToList();

One possible solution would look like this:
var persons = new List<Person>
{
new Person { Age = 20 },
new Person { Age = 30 },
new Person { Age = 20 },
new Person { Age = 40 },
new Person { Age = 50 },
new Person { Age = 50 },
new Person { Age = 20 }
};
var result = from person in persons
group person by person.Age into g
select g.ToList();
var listOfListOfPersons = result.ToList();
This would create a List holding List<Person> items.
Yet another possibility would be to create a class representing the (strongly typed) List<List<Person>> structure:
public class Persons
{
public List<Person> Entries { get; set; }
}
public class Person
{
public Int32 Age { get; set; }
}
and fill it like this:
var result = from person in persons
group person by person.Age into g
select new Persons
{
Entries = g.ToList()
};
var listOfListOfPersons = result.ToList();

Related

Assign value when calling method Syntax in C#

I am sorry I don't know if C# has this syntax or not and I don't know the syntax name. My code below, I want to add 2 people has the same name but not age. So I am wondering if C# has a brief syntax that I can change the Age property value when calling AddPerson method. Please tell me if I can do that? If can, how can I do? Thank you.
class Program
{
static void Main(string[] args)
{
//I want to do this
Person p = new Person
{
Name = "Name1",
//And other propeties
};
AddPerson(p{ Age = 20});
AddPerson(p{ Age = 25}); //Just change Age; same Name and others properties
//Not like this(I am doing)
p.Age = 20;
AddPerson(p);
p.Age = 25;
AddPerson(p);
//Or not like this
AddPerson(new Person() { Name = "Name1", Age = 20 });
AddPerson(new Person() { Name = "Name1", Age = 25 });
}
static void AddPerson(Person p)
{
//Add person
}
}
class Person
{
public string Name { get; set; }
public int Age { get; set; }
//And more
}
If Person is a Record-Type, then yes: by using C# 9.0's new with operator. Note that this requires C# 9.0, also note that Record-Types are immutable.
public record Person( String Name, Int32 Age );
public static void Main()
{
Person p = new Person( "Name1", 20 );
List<Person> people = new List<Person>();
people.Add( p with { Age = 25 } );
people.Add( p with { Age = 30 } );
people.Add( p with { Name = "Name2" } );
}
This is what I see in LinqPad when I run it:

group by and merge some field result in linq

I have a class like
public class Person
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
Now I have a list of this class: List<Person> persons;
var persons = new List<Person> {
new Person { Id = 1, LastName = "Reza", FirstName="Jenabi" },
new Person { Id = 1, LastName = "Amin", FirstName="Golmahalle"},
new Person { Id = 2, LastName = "Hamed", FirstName="Naeemaei"}
};
Is there a way I can group by Id and get the list of all the full Name (Combine first and last names)?
So after grouping:
var Id = results[0].Id; // Output : 1
List<string> fullNames = results[0].FullNames; // Output : "Reza Jenabi","Amin Golmahalle"
I believe this is what you need:
var results = persons.GroupBy(x => x.Id)
.Select(x => new { Id = x.Key, FullNames = x.Select(p => $"{p.FirstName} {p.LastName}").ToList() })
.ToList();
I think bellow code can help you:
var fff = from p in persons
group $"{p.FirstName} {p.LastName}" by p.Id into g
select new { PersonId = g.Key, FullNames = g.ToList() };
yeah, you can use GroupBy and Join those items:
var grouped = persons.GroupBy(p => p.Id)
.Select(s => string.Join(", ", s.Select(a=> $"{a.FirstName} {a.LastName}")));

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

LINQ to Get All heirerichal children

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

Refer to other properties of the object you are initializing in an object initializer

When creating objects like:
var customers = new Customers
{
Persons = new List<Person>
{
new Person { CustomId = "111" },
new Person { CustomId = "222" }
},
Organizations = new List<Organization>
{
new Organization { CustomId = "333" }
},
Keys = new HashSet<string> { "111", "222", "333" }
};
I want to change the initialization of Keys to use the previous value from Person[0].CustomId, Person[1].CustomId and Organization[0].CustomId. (Not hardcoded like this "111", "222", "333")
Is there a simple way to do this inside this type of initialization? I can add the keys after the initialization of customers like this:
foreach (var person in customers.Persons)
{
customers.Keys.Add(person.CustomId);
}
foreach (var org in customers.Organizations)
{
customers.Keys.Add(org.CustomId);
}
But I cannot create Keys from Person and Organization properties in the same initialization as customers?
No, you can't do that.
You can create the collections first, and concatenate the selected keys:
var persons = new List<Person>
{
new Person { CustomId = "111" },
new Person { CustomId = "222" }
};
var organizations = new List<Organization>
{
new Organization { CustomId = "333" }
};
var keys = persons.Select(p => p.CustomId)
.Concat(organizations.Select(o => o.CustomId));
var customers = new Customers
{
Persons = persons,
Organizations = organizations,
Keys = new HashSet<string>(keys),
}
But like others said, there's more issues with this implementation. Do you want to use this syntax for every consumer of the Customers class? How will you keep the key collection up to date with the Persons and Organizations collections, like when you add or remove items from them?
public class Customers
{
public List<Person> Persons { get; set; }
public List<Organization> Organizations { get; set; }
public Customers()
{
Persons = new List<Person>();
Organizations = new List<Organization>();
}
public Customers(IEnumerable<Person> persons,
IEnumerable<Organization> organizations)
: this()
{
Persons.AddRange(persons);
Organizations.AddRange(organizations);
}
public IEnumerable<string> Keys
{
return Persons.Select(p => p.CustomId)
.Concat(Organizations.Select(o => o.CustomId));
}
}
Then a call site may look like this:
var persons = new List<Person>
{
new Person { CustomId = "111" },
new Person { CustomId = "222" }
};
var organizations = new List<Organization>
{
new Organization { CustomId = "333" }
};
var customers = new Customers(persons, organizations);
var keys = customers.Keys;
Make Keys into a public property like:
public HashSet<string> Keys
{
get
{
// create and return a new hashset from persons and organizations
}
}
This way you newer have to think about updating your Keys member.

Categories