How to make aggregate function in single method using linq? - c#

I have class like this
public class Student
{
public int Id { get; set; }
public string Name { get; set; }
public string Gender { get; set; }
public GRADE Grade { get; set; }
public string Nationality { get; set; }
}
public enum GRADE
{
A = 0,
B = 1,
C = 2,
D = 3,
E = 4
}
var list = new List<Student>();
list.Add(new Student() { Id = 1, Name = "Prasad", Gender = "M", Nationality = "India", Grade = GRADE.A });
list.Add(new Student() { Id = 2, Name = "Raja", Gender = "M", Nationality = "India", Grade = GRADE.B });
list.Add(new Student() { Id = 3, Name = "Hindu", Gender = "F", Nationality = "India", Grade = GRADE.A });
list.Add(new Student() { Id = 4, Name = "Hamed", Gender = "M", Nationality = "India", Grade = GRADE.C });
list.Add(new Student() { Id = 5, Name = "Priya", Gender = "F", Nationality = "India", Grade = GRADE.D });
list.Add(new Student() { Id = 6, Name = "Meera", Gender = "F", Nationality = "India", Grade = GRADE.B });
I got the solution like this, For each expression i want to write bunch of code.. Sum,Avg,Count etc
Linq Expressions
//count
var c = (from x in list.GroupBy(k => k.Gender)
select new
{
category = x.Key,
Value = x.Count()
}).ToList();
//sum
var s = (from x in list.GroupBy(k => k.Gender)
select new
{
category = x.Key,
Value = x.Sum(k => (int)k.Grade)
}).ToList();
//avg
var a = (from x in list.GroupBy(k => k.Gender)
select new
{
category = x.Key,
Value = x.Average(k => (int)k.Grade)
}).ToList();
I am trying to make one function, based on the aggregate function; it should return the value, I tried I could not find it.

One issue you have is that all three aggregates do not have the same return type, also if you use a function then the return type would have to be object because you are returning an anonymous type.
The closest I could get to what I think you want was this;
Step 1: create a new type;
public class AggregateValue<T>
{
public string Category { get; set; }
public T Value { get; set; }
}
Step 2: Create a function that returns a collection of this type and accepts a Func as a parameter that will calculate your different aggregates;
IEnumerable<AggregateValue<T>> GetAggregateValues<T>(List<Student> students, Func<IEnumerable<Student>, T> aggregateFunction)
{
return (from x in students.GroupBy(k => k.Gender)
select new AggregateValue<T>
{
Category = x.Key,
Value = aggregateFunction(x)
}).ToList();
}
You can use it like this;
var list = new List<Student>();
list.Add(new Student() { Id = 1, Name = "Prasad", Gender = "M", Nationality = "India", Grade = GRADE.A });
list.Add(new Student() { Id = 2, Name = "Raja", Gender = "M", Nationality = "India", Grade = GRADE.B });
list.Add(new Student() { Id = 3, Name = "Hindu", Gender = "F", Nationality = "India", Grade = GRADE.A });
list.Add(new Student() { Id = 4, Name = "Hamed", Gender = "M", Nationality = "India", Grade = GRADE.C });
list.Add(new Student() { Id = 5, Name = "Priya", Gender = "F", Nationality = "India", Grade = GRADE.D });
list.Add(new Student() { Id = 6, Name = "Meera", Gender = "F", Nationality = "India", Grade = GRADE.B });
var sumGrades = new Func<IEnumerable<Student>, int>(p => p.Sum(l => (int)l.Grade));
var aveGrades = new Func<IEnumerable<Student>, double>(p => p.Average(k => (int)k.Grade));
var count = new Func<IEnumerable<Student>, int>(p => p.Count());
var c = GetAggregateValues(list, count);
var s = GetAggregateValues(list, sumGrades);
var a = GetAggregateValues(list, aveGrades);

You can combine all your aggregations in one statement:
var result = (from x in list.GroupBy(k => k.Gender)
select new
{
category = x.Key,
Count = x.Count(),
Sum = x.Sum(k => (int)k.Grade),
Average = x.Average(k => (int)k.Grade)
}).ToList();

Related

How to get data from 3 table into 1 list

Sorry for my bad English.
Here is my SQL Design.
I have 3 table in Sqlsever. Each table has 4 column with same name, same datatype.
And i want to get data from 4 column "Id, Name, Quantity, IdCategory" from 3 table into 1 list object same as returning value in this code below:
public async Task<IEnumerable<Shirt>> LoadAllShirt()
{
return await _dbContext.Shirt.ToListAsync();
}
I use .NET Core 6 Mvc - code first. Thanks for your help.
I have 3 table in Sqlsever. Each table has 4 column with same name,
same datatype. And I want to get data from 4 column "Id, Name,
Quantity, IdCategory" from 3 table into 1 list, I use .NET Core 6 Mvc - code first.
Well, lot of way around to handle this kind of scenario. Most easy and convenient way I would prefer to use View model or using Linq query.
Lets assume you have below Models:
Models:
public class Bags
{
public int Id { get; set; }
public string Name { get; set; }
public int Quantity { get; set; }
public string Category { get; set; }
}
public class Shirts
{
public int Id { get; set; }
public string Name { get; set; }
public int Quantity { get; set; }
public string Category { get; set; }
}
public class Shoes
{
public int Id { get; set; }
public string Name { get; set; }
public int Quantity { get; set; }
public string Category { get; set; }
}
Seeds In Models:
List<Bags> listBags = new List<Bags>();
listBags.Add(new Bags() { Id = 101, Name = "Bag A", Quantity =10, Category = "Cat-A"});
listBags.Add(new Bags() { Id = 102, Name = "Bag B", Quantity =15, Category = "Cat-A"});
listBags.Add(new Bags() { Id = 103, Name = "Bag C", Quantity =20, Category = "Cat-A"});
List<Shirts> listShirts = new List<Shirts>();
listShirts.Add(new Shirts() { Id = 101, Name = "Shirt A", Quantity = 10, Category = "Cat-B" });
listShirts.Add(new Shirts() { Id = 102, Name = "Shirt B", Quantity = 15, Category = "Cat-B" });
listShirts.Add(new Shirts() { Id = 103, Name = "Shirt C", Quantity = 20, Category = "Cat-B" });
List<Shoes> listShoes = new List<Shoes>();
listShoes.Add(new Shoes() { Id = 101, Name = "Shirt A", Quantity = 10, Category = "Cat-S" });
listShoes.Add(new Shoes() { Id = 102, Name = "Shirt B", Quantity = 15, Category = "Cat-S" });
listShoes.Add(new Shoes() { Id = 103, Name = "Shirt C", Quantity = 20, Category = "Cat-S" });
Way: 1 using ViewModel:
public class AllViewModel
{
public List<Bags> Bags { get; set; }
public List<Shirts> Shirts { get; set; }
public List<Shoes> Shoes { get; set; }
}
Query Using ViewModel:
var allTableUsingViewModel = new AllViewModel();
allTableUsingViewModel.Bags = listBags;
allTableUsingViewModel.Shirts = listShirts;
allTableUsingViewModel.Shoes = listShoes;
Output Using ViewModel:
Way: 2 using Linq Annonymous Type:
Query Using Linq Annonymous Type:
var AllTableListUsingLinq = from a in listBags
join b in listShirts on a.Id equals b.Id
join c in listShoes on b.Id equals c.Id
select new
{
FromBagsID = a.Id,
FromBagsName = a.Name,
FromBagsQuantity = a.Quantity,
FromBagsCategory = a.Category,
FromShirtsID = b.Id,
FromShirtsName = b.Name,
FromShirtsQuantity = b.Quantity,
FromShirtsCategory = b.Category,
FromShoesID = c.Id,
FromShoesName = c.Name,
FromShoesQuantity = c.Quantity,
FromShoesCategory = c.Category
};
Output Using Linq Annonymous Type:
Full Controller:
[HttpGet("GetFrom3Tables")]
public IActionResult GetFrom3Tables()
{
List<Bags> listBags = new List<Bags>();
listBags.Add(new Bags() { Id = 101, Name = "Bag A", Quantity =10, Category = "Cat-A"});
listBags.Add(new Bags() { Id = 102, Name = "Bag B", Quantity =15, Category = "Cat-A"});
listBags.Add(new Bags() { Id = 103, Name = "Bag C", Quantity =20, Category = "Cat-A"});
List<Shirts> listShirts = new List<Shirts>();
listShirts.Add(new Shirts() { Id = 101, Name = "Shirt A", Quantity = 10, Category = "Cat-B" });
listShirts.Add(new Shirts() { Id = 102, Name = "Shirt B", Quantity = 15, Category = "Cat-B" });
listShirts.Add(new Shirts() { Id = 103, Name = "Shirt C", Quantity = 20, Category = "Cat-B" });
List<Shoes> listShoes = new List<Shoes>();
listShoes.Add(new Shoes() { Id = 101, Name = "Shirt A", Quantity = 10, Category = "Cat-S" });
listShoes.Add(new Shoes() { Id = 102, Name = "Shirt B", Quantity = 15, Category = "Cat-S" });
listShoes.Add(new Shoes() { Id = 103, Name = "Shirt C", Quantity = 20, Category = "Cat-S" });
//Way: 1 Linq Query
var AllTableListUsingLinq = from a in listBags
join b in listShirts on a.Id equals b.Id
join c in listShoes on b.Id equals c.Id
select new
{
FromBagsID = a.Id,
FromBagsName = a.Name,
FromBagsQuantity = a.Quantity,
FromBagsCategory = a.Category,
FromShirtsID = b.Id,
FromShirtsName = b.Name,
FromShirtsQuantity = b.Quantity,
FromShirtsCategory = b.Category,
FromShoesID = c.Id,
FromShoesName = c.Name,
FromShoesQuantity = c.Quantity,
FromShoesCategory = c.Category
};
//Way: 2 : ViewModel
var allTableUsingViewModel = new AllViewModel();
allTableUsingViewModel.Bags = listBags;
allTableUsingViewModel.Shirts = listShirts;
allTableUsingViewModel.Shoes = listShoes;
return Ok(AllTableListUsingLinq);
}
Note: If you need more information you could check our official document for View Model and Linq Projction here
The following sample query will list your 3 types of data into a single result set.
var allResults = resultSet1.Concat(resultSet2);
For the return type create a class which will be the parent class for all your products (Bag,Shirt,Shoes) Which will help you to return data in a single Generic data.
If you use any non-generic list to send the data like hashtable or Arraylist then then there will be no issue.
In my way I will suggest to use generic data list as it will help you fetch data in better time complexity.
In this case you may need to define additional indirect base class with these 4 parameters. Than you can create Collection of this base class, and concatinate all 3 tables into.
public class BaseEntity
{
public string Name {get;set;}
}
public class Shoes : BaseEntity
{
}
...
public IEnumerable<BaseEntity> GetAllTables()
{
var shirts = await _dbContext.Shirt.ToListAsync();
var shoes = await _dbContext.Shoes.ToListAsync();
var bags = await _dbContext.Bags.ToListAsync();
return shirts.Concat(shoes).Concat(bags);
}
Similar example but witout casting to base class is shown in Enumerable.Concat documentation: https://learn.microsoft.com/pl-pl/dotnet/api/system.linq.enumerable.concat?view=net-7.0

How to make nested list from one base list

I would like to make nested list from this one list
public class Product
{
string Id;
string ProductName;
decimal Price;
string Supplier;
int Quantity;
string VersionId;
string TypeId;
}
public class ProductTypeDto
{
public string TypeId { get; set; }
public string ProductName { get; set; }
public string Price { get; set; }
public List<ProductVersionDto> ProductVersions { get; set; }
}
public class ProductVersionDto
{
public string VersionId { get; set; }
public string ProductName { get; set; }
public string Supplier { get; set; }
public int Quantity { get; set; }
}
I would like to know how I can create a list of ProductTypeDto using linq c# .
I need to get all products with the same TypeId , and ProductVersions field should contain products with same version Id (and of course same TypeId).
I don't want to use foreach loops, and I dont want to loop over products twice to make this nested list.. I think there would be a better way using Linq, would be great if you can help..
Edit: I add here what I have done so far, but it is not the way I want this to be fixed.
List<ProductTypeDto> products = this._dbContext
.Products
.Where(product => product.TypeId == query.TypeId)
.Select(product => new ProductTypeDto()
{
TypeId = product.TypeId,
ProductName = product.ProductName,
Price = product.Price,
ProductVersions = product.Products.Where(p => p.TypeId == product.TypeId)
.Select(p => new ProductVersionDto()
{
VersionId = p.VersionId,
ProductName = p.ProductName,
Supplier = p.Supplier,
Quantity = p.Quantity
}).ToList()
})
.ProjectTo<ProductTypeDto>(this._config)
.ToListAsync(cancellationToken);
This is the result I want to get:
var product1 = new Product() { Id = 1, ProductName = "foo", Price = 20, Supplier = "test1", Quantity = 3, VersionId = "1", TypeId = "1" };
var product2 = new Product() { Id = 2, ProductName = "foo1", Price = 60, Supplier = "test2", Quantity = 9, VersionId = "1", TypeId = "1" };
var product3 = new Product() { Id = 3, ProductName = "foo2", Price = 30, Supplier = "test3", Quantity = 5, VersionId = "2", TypeId = "1" };
var product4 = new Product() { Id = 4, ProductName = "foo3", Price = 10, Supplier = "test4", Quantity = 4, VersionId = "1", TypeId = "2" };
var product5 = new Product() { Id = 5, ProductName = "foo4", Price = 50, Supplier = "test5", Quantity = 8, VersionId = "1", TypeId = "3" };
List<ProductVersionDto> p1 = {
new ProductVersionDto { ProductName = "foo", Quantity= 3, Supplier ="test1"}
new ProductVersionDto { ProductName = "foo1", Quantity= 9, Supplier ="test2"}
};
List<ProductVersionDto> p2 = {
new ProductVersionDto { ProductName = "foo3", Quantity= 4, Supplier ="test4"}
};
List<ProductVersionDto> p3 = {
new ProductVersionDto { ProductName = "foo4", Quantity= 8, Supplier ="test5"}
};
List<ProductTypeDto> products = {
new ProductTypeDto{ ProductName = "foo", Price =20, ProductVersions = p1}
new ProductTypeDto{ ProductName = "foo1", Price =60, ProductVersions = p1}
new ProductTypeDto{ ProductName = "foo3", Price =10, ProductVersions = p2}
new ProductTypeDto{ ProductName = "foo4", Price =50, ProductVersions = p3}
}
Try this:
var dtos =
(
from p in products
group new ProductVersionDto()
{
VersionId = p.VersionId,
ProductName = p.ProductName,
Supplier = p.Supplier,
Quantity = p.Quantity
} by new { p.TypeId, p.ProductName, p.Price } into g
select new ProductTypeDto()
{
TypeId = g.Key.TypeId,
ProductName = g.Key.TypeId,
Price = g.Key.Price,
ProductVersions = g.ToList(),
}
).ToList();
It appears that you meant Price to be a decimal in ProductTypeDto, BTW.

Determining if an object with specific value is repeated in a list

I have a Customer class with First and Last name attributes. Also, I have a list of customers.
public static List<Customer> customersList = new List<Customer>();
Given a first and last name, what's the cleanest way to find out if this name appears on the list more than once?
Define class
public class Customer
{
public string FName { get; set; }
public string LName { get; set; }
public int Count { get; set; }
}
Query here
List<Customer> list = new List<Customer>();
list.Add(new Customer() { FName = "A", LName = "L" });
list.Add(new Customer() { FName = "A", LName = "L" });
list.Add(new Customer() { FName = "B", LName = "L" });
list.Add(new Customer() { FName = "B", LName = "L" });
list.Add(new Customer() { FName = "C", LName = "L" });
var isDup = list.Where(x => x.FName.Equals("A") && x.LName.Equals("L")).Count() > 1;
var isNotDup = list.Where(x => x.FName.Equals("C") && x.LName.Equals("L")).Count() > 1;
Result
isDup is true
isNotDup is false

How to sort in Linq C#?

I encountered a ridiculous problem using Linq into C# with my database included.
I tried so hard to sort by the name who got the higher rank of occurence.
Such as :
Toto : 1 occurence.
Titi : 3 occurences.
fonction.
consolewriteLine(the one who got the higher score of occurences )
So, it will be Titi.
But now in Linq, this is my code :
public static void AfficherLePrenomMasculinLePlusFrequentParmiLesElus()
{
using (var context = new electionEDM3())
{
var queryPrenomMasculin = from listedesprenomsM in context.Candidat
where listedesprenomsM.sexe == "M"
select listedesprenomsM;
var prenomMasculinTrouve = from prenommasculintrouve in queryPrenomMasculin
orderby prenommasculintrouve.prenom
group prenommasculintrouve by prenommasculintrouve.prenom into nombredeprenommasculintrouve
select new
{
prenom = nombredeprenommasculintrouve.Key,
count = nombredeprenommasculintrouve.Count()
};
foreach (var PrenomMPlusSouventPresent in prenomMasculinTrouve)
{
int i = 0;
int BestPrenomM = 0;
string MeilleurPrenomMasculin = "";
if ( PrenomMPlusSouventPresent.count > BestPrenomM)
{
BestPrenomM = PrenomMPlusSouventPresent.count;
MeilleurPrenomMasculin = PrenomMPlusSouventPresent.prenom;
BestPrenomM++;
}
Console.WriteLine(BestPrenomM);
}
}
}
As a result, i only got all the occurences of the " Prenom ". And not the one who got the higher score.
What should i do ?
Thank for your answers !
you are ordering using prenommasculintrouve.prenom and after that you are grouping, that is useless. You should order the result of the grouped data, using the count property of your anonymous type (prenom, count properties)
Your order by in your initial grouping is unnecessary.
Get the grouping first, then order by count descending, and take the first value. That should be the result you need.
I will give you a simple example
public class Person
{
public string Sex { get; set; }
public string name { get; set; }
}
private static void Linq2()
{
var namesList = new List<Person>();
namesList.Add(new Person() { Sex = "M", name = "Jonh" });
namesList.Add(new Person() { Sex = "M", name = "James" });
namesList.Add(new Person() { Sex = "F", name = "Maria" });
namesList.Add(new Person() { Sex = "F", name = "Cindy" });
namesList.Add(new Person() { Sex = "M", name = "Jim" });
namesList.Add(new Person() { Sex = "F", name = "Helen" });
namesList.Add(new Person() { Sex = "M", name = "Jonh" });
namesList.Add(new Person() { Sex = "F", name = "Maria" });
namesList.Add(new Person() { Sex = "F", name = "Cindy" });
namesList.Add(new Person() { Sex = "M", name = "Jonh" });
var grouped = from personItem in namesList
group personItem by personItem.name into personGroup
select new
{
name = personGroup.Key,
count = personGroup.Count()
};
// here you order your list Descending in order the name
// with the most Ocurrance will be on the top and select the First
var nameMaxOcurrance = grouped.OrderByDescending(x => x.count).First().name;
var maxOcurrance = grouped.Max(x => x.count);
Console.WriteLine("Name with Max Ocurrances:" + nameMaxOcurrance);
Console.WriteLine("Max Ocurrances:" + maxOcurrance);
}
In your case
var nameMaxOcurrance = prenomMasculinTrouve.OrderByDescending(x => x.count).First().prenom;
var maxOcurrances = prenomMasculinTrouve.Max(x => x.count);

How would I pivot this object using linq?

If I have the following objects.
public class CFS
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public IList<Topic> Topics { get; set; }
public IList<Status> Status { get; set; }
}
public class Topic
{
public int ID { get; set; }
public string Name { get; set; }
}
public class Status
{
public int ID { get; set; }
public string Name { get; set; }
}
How can I put it into the following object where Topic.ID == Status.ID && Status.Name = "pass"? The Topic and Status string values would be the Topic.Name and Status.Name values respectively. The list of string can be the FirstName, email, whatever, that part is trivial. I realize Topic and Status expose the same properties but that's just for this example.
public class SelectedTopic
{
public string Topic { get; set; }
public string Status { get; set; }
public IList<string> Person { get; set; }
}
I've tried several combinations of SelectMany, Any, Join and I can't seem to pivot the data the way I want.
I don't know why you would want to do this but here is how:
void Main()
{
List<Topic> topicA = new List<Topic>() { new Topic() { ID = 1, Name = "1" }, new Topic() {ID = 2 , Name = "2"}, new Topic() {ID = 3, Name = "3" } };
List<Topic> topicB = new List<Topic>() { new Topic() { ID = 2, Name = "2" }, new Topic() {ID = 3 , Name = "3"}, new Topic() {ID = 4, Name = "4" } };
List<Topic> topicC = new List<Topic>() { new Topic() { ID = 1, Name = "1" } };
List<Topic> topicD = new List<Topic>() { new Topic() {ID = 2 , Name = "2"}, new Topic() {ID = 3, Name = "3" } };
List<Status> statusA = new List<Status>() { new Status() { ID = 1, Name = "pass" }, new Status() {ID = 2 , Name = "2"}, new Status() {ID = 3, Name = "3" } };
List<Status> statusB = new List<Status>() { new Status() { ID = 2, Name = "2" }, new Status() {ID = 3 , Name = "pass"}, new Status() {ID = 4, Name = "pass" } };
List<Status> statusC = new List<Status>() { new Status() { ID = 1, Name = "pass" } };
List<Status> statusD = new List<Status>() { new Status() {ID = 2 , Name = "2"}, new Status() {ID = 3, Name = "pass" } };
List<CFS> test = new List<CFS>() {
new CFS() { FirstName = "A", LastName = "A", Email = "A#A.com", Topics = topicA, Status = statusA },
new CFS() { FirstName = "B", LastName = "B", Email = "B#B.com", Topics = topicB, Status = statusB },
new CFS() { FirstName = "C", LastName = "C", Email = "C#C.com", Topics = topicC, Status = statusC },
new CFS() { FirstName = "D", LastName = "D", Email = "D#D.com", Topics = topicD, Status = statusD },
};
var result = test.SelectMany(x => x.Topics.SelectMany((t) => x.Status, (topic,status) => new { CFS = x, T = topic, S = status }))
.Where(x => x.S.Name == "pass" && x.T.ID == x.S.ID)
.Select(x => new { first = x.CFS.FirstName, status = x.S.Name, topic = x.T.Name})
.GroupBy(x => x.topic)
.Select(x => new SelectedTopic { Topic = x.Key, Status = "pass", Person = x.Select(z => z.first).Distinct().ToList() })
.Dump();
}
Tested in LinqPad -- if you are not using this tool I suggest you do so.

Categories

Resources