Linq Group By on Sets - c#

While working with Linq on Grouping sets, I found a problem while the query is returning the list.
My Code is written in LinqPad
public static void Main()
{
List<RequestEntry> RequestEntries = new List<RequestEntry>(){
new RequestEntry{
RequestId = 1,
RequestedDate = new DateTime(2018, 06, 01),
ApproverUserId = "STEVES",
ApproverUserName = "Steve Smith",
AuthorizerUserId = "JAMESS",
AuthorizerUserName = "James Sutherland"
},
new RequestEntry{
RequestId = 1,
RequestedDate = new DateTime(2018, 06, 01),
ApproverUserId = "GRAHAMS",
ApproverUserName = "Graham Smith",
AuthorizerUserId = "THABANGM",
AuthorizerUserName = "Thabang Moroe"
},
new RequestEntry{
RequestId = 2,
RequestedDate = new DateTime(2018, 06, 02),
ApproverUserId = "STEVES",
ApproverUserName = "Steve Smith",
AuthorizerUserId = "JAMESS",
AuthorizerUserName = "James Sutherland"
},
new RequestEntry{
RequestId = 3,
RequestedDate = new DateTime(2018, 06, 03),
ApproverUserId = "ROBINS",
ApproverUserName = "Robin Smith",
AuthorizerUserId = "TOMH",
AuthorizerUserName = "Tom Harrision"
},
new RequestEntry{
RequestId = 3,
RequestedDate = new DateTime(2018, 06, 03),
ApproverUserId = "CHRISS",
ApproverUserName = "Chris Smith",
AuthorizerUserId = "TOMH",
AuthorizerUserName = "Tom Harrision"
},
new RequestEntry{
RequestId = 3,
RequestedDate = new DateTime(2018, 06, 03),
ApproverUserId = "LIAMS",
ApproverUserName = "Liam Smith",
AuthorizerUserId = "TOMH",
AuthorizerUserName = "Tom Harrision"
}
};
var results = (
from r in RequestEntries
group r by new
{
r.RequestId, r.RequestedDate
} into g
select new RequestWithApprover(){
RequestId = g.Key.RequestId,
RequestedDate = g.Key.RequestedDate,
ApproverUserIds = g.Select(c => c.ApproverUserId).ToList(),
RequestApprovers = g.Select(c => new RequestApprover(){
ApproverUserName = c.ApproverUserName,
ApproverUserId = c.ApproverUserId
}).ToList(),
RequestAuthorizers = g.Select(c => new RequestAuthorizer(){
AuthorizerUserName = c.AuthorizerUserName,
AuthorizerUserId = c.AuthorizerUserId
}).ToList()
}).ToList();
results.Dump();
}
public class RequestEntry
{
public int RequestId
{
get;
set;
}
public string ApproverUserId
{
get;
set;
}
public string ApproverUserName
{
get;
set;
}
public string AuthorizerUserId
{
get;
set;
}
public string AuthorizerUserName
{
get;
set;
}
public DateTime RequestedDate
{
get;
set;
}
}
public class RequestApprover
{
public string ApproverUserId
{
get;
set;
}
public string ApproverUserName
{
get;
set;
}
}
public class RequestAuthorizer
{
public string AuthorizerUserId
{
get;
set;
}
public string AuthorizerUserName
{
get;
set;
}
}
public class RequestWithApprover
{
public int RequestId
{
get;
set;
}
public DateTime RequestedDate
{
get;
set;
}
public List<string> ApproverUserIds
{
get;
set;
}
public List<RequestApprover> RequestApprovers
{
get;
set;
}
public List<RequestAuthorizer> RequestAuthorizers
{
get;
set;
}
}
What I found is, For RequestID =3, the Number of RequestAuthorizer items should be 1, but it had multiplied. I found that in RequestID =3 the Maximum items in any list is 3, the RequestAuthorizer to multiplies to that Number. Refer below snapshot highlighted of the result:
Technically, In RequestId=3 the List<RequestAuthorizer> should only contain the Item of Tom Harrision and the count should be one.
How could I correct it?

How about grouping the RequestAuthorizer?
var results = (
from r in RequestEntries
group r by new
{
r.RequestId, r.RequestedDate
} into g
select new RequestWithApprover(){
RequestId = g.Key.RequestId,
RequestedDate = g.Key.RequestedDate,
ApproverUserIds = g.Select(c => c.ApproverUserId).ToList(),
RequestApprovers = g.Select(c => new RequestApprover(){
ApproverUserName = c.ApproverUserName,
ApproverUserId = c.ApproverUserId
}).ToList(),
RequestAuthorizers = g.
GroupBy(g1 => new {
g1.AuthorizerUserName,
g1.AuthorizerUserId }).
Select(c => new RequestAuthorizer(){
AuthorizerUserName = c.Key.AuthorizerUserName,
AuthorizerUserId = c.Key.AuthorizerUserId
}).ToList()
}).ToList();

Related

Sort 2 tables at the same time via using LinQ

My example contains 2 tables Book and Comment.
Book:
Id Name UserId DateTime
B1 Book1 User1 16/11/2016 11:15:00
B2 Book2 User1 16/11/2016 12:15:00
B3 Book3 User2 16/11/2016 10:15:00
Comment:
Id BookId UserId DateTime
C1 B3 User1 16/11/2016 11:17:00
C2 B1 User1 16/11/2016 11:16:00
List of Book via specific user id:
string userid = "User1";
IEnumerable<Book> books = _context.Books.Where(x => x.UserId == userid);
List of Comment:
IEnumerable<Book> comments = _context.Comments.Where(x => x.UserId == userid);
In activity history (web page), I want to show all books and comments what I have collected via userid, one by one. But, how can I sort 2 objects again via .OrderByDescending(x => x.DateTime)?
My goal:
User1 just postes a new book to the store:
B2 Book2 User1 16/11/2016 12:15:00
Before that, he posted a comment to his own thread:
C1 B1 User1 16/11/2016 11:17:00
Earlier, he posted a comment in another thread (UserId == "User2"):
C2 B3 User1 16/11/2016 11:16:00
Older, he posted a new book to the store:
B1 Book1 User1 16/11/2016 11:15:00
I can classify them via DateTime column (on paper):
16/11/2016 12:15:00
16/11/2016 11:17:00
16/11/2016 11:16:00
16/11/2016 11:15:00
How can I sort it?
UPDATE:
I just found a solution but LinQ. I want a solution using LinQ with same result (for shorter):
namespace MP
{
public class Book
{
public string Id { get; set; }
public string Name { get; set; }
public string UserId { get; set; }
public DateTime DateTime { get; set; }
}
public class Comment
{
public string Id { get; set; }
public string BookId { get; set; }
public string UserId { get; set; }
public string Content { get; set; }
public DateTime DateTime { get; set; }
}
public class Result
{
public object Object { get; set; }
public DateTime DateTime { get; set; }
}
class Program
{
static void Main()
{
try
{
string userid = "User1";
var books = new List<Book>();
books.Add(new Book { Id = "B1", Name = "Book1", UserId = "User1", DateTime = new DateTime(2016, 11, 16, 11, 15, 00) });
books.Add(new Book { Id = "B2", Name = "Book2", UserId = "User1", DateTime = new DateTime(2016, 11, 16, 12, 15, 00) });
books.Add(new Book { Id = "B3", Name = "Book3", UserId = "User2", DateTime = new DateTime(2016, 11, 16, 10, 15, 00) });
var comments = new List<Comment>();
comments.Add(new Comment { Id = "c1", BookId = "B3", UserId = "User1", Content = "cmt1", DateTime = new DateTime(2016, 11, 16, 11, 17, 00) });
comments.Add(new Comment { Id = "c2", BookId = "B1", UserId = "User1", Content = "cmt2", DateTime = new DateTime(2016, 11, 16, 11, 16, 00) });
var result = new List<Result>();
books.ForEach(x =>
{
if (x.UserId == userid)
{
result.Add(new Result { Object = x, DateTime = x.DateTime });
}
});
comments.ForEach(x =>
{
if (x.UserId == userid)
{
result.Add(new Result { Object = x, DateTime = x.DateTime });
}
});
result = result.OrderByDescending(x => x.DateTime).ToList();
foreach (var item in result)
{
Type type = item.Object.GetType();
if (type == typeof(MP.Book))
{
var book = (Book)item.Object;
Console.WriteLine($"Book: Id: {book.Id} - Name: {book.Name} - DateTime: {book.DateTime}");
}
if (type == typeof(MP.Comment))
{
var cmt = (Comment)item.Object;
Console.WriteLine($"Comment: Id: {cmt.Id} - Content: {cmt.Content} - DateTime: {cmt.DateTime}");
}
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
}
}
Result:
Here's solution that should work for you. Basically I took your idea of objectifying the data and creating a linq query that creates a list of objects from each list and sorts them according to the DateTime. By overriding the ToString() method printing the contents of the list becomes very simple:
public class Book
{
public const string className = "Book";
public string Id { get; set; }
public string Name { get; set; }
public string UserId { get; set; }
public DateTime DateTime { get; set; }
public override string ToString()
{
return $"Book: Id: {Id.PadRight(7)} - Name: {Name.PadRight(14)} - DateTime: {DateTime}";
}
}
public class Comment
{
public string Id { get; set; }
public string BookId { get; set; }
public string UserId { get; set; }
public string Content { get; set; }
public DateTime DateTime { get; set; }
public override string ToString()
{
return $"Comment: Id: {Id.PadRight(7)} - Content: {Content.PadRight(11)} - DateTime: {DateTime}";
}
}
class Program
{
static void Main()
{
try
{
string userid = "User1";
var books = new List<Book>();
books.Add(new Book { Id = "B1", Name = "Book1", UserId = "User1", DateTime = new DateTime(2016, 11, 16, 11, 15, 00) });
books.Add(new Book { Id = "B2", Name = "Book2", UserId = "User1", DateTime = new DateTime(2016, 11, 16, 12, 15, 00) });
books.Add(new Book { Id = "B3", Name = "Book3", UserId = "User2", DateTime = new DateTime(2016, 11, 16, 10, 15, 00) });
var comments = new List<Comment>();
comments.Add(new Comment { Id = "c1", BookId = "B3", UserId = "User1", Content = "cmt1", DateTime = new DateTime(2016, 11, 16, 11, 17, 00) });
comments.Add(new Comment { Id = "c2", BookId = "B1", UserId = "User1", Content = "cmt2", DateTime = new DateTime(2016, 11, 16, 11, 16, 00) });
var test = (from b in books
where b.UserId == userid
select (object)b).Concat
(from c in comments
where c.UserId == userid
select (object)c).OrderBy(x => x.GetType() == typeof(Book)?((Book)x).DateTime:((Comment)x).DateTime);
foreach(var o in test)
{
Console.WriteLine(o);
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
}
Did some more thinking, using generic objects always makes me uneasy, and came up with a better solution. by using a base class with the common properties and the book and comment classes deriving from that, the list you make can contain either book or comment and it can be filtered and sorted by any of the base class properties:
public class Item
{
public string Id { get; set; }
public string UserId { get; set; }
public DateTime DateTime { get; set; }
}
public class Book:Item
{
public string Name { get; set; }
public override string ToString()
{
return $"Book: Id: {Id.PadRight(7)} - Name: {Name.PadRight(14)} - DateTime: {DateTime}";
}
}
public class Comment:Item
{
public string BookId { get; set; }
public string Content { get; set; }
public override string ToString()
{
return $"Comment: Id: {Id.PadRight(7)} - Content: {Content.PadRight(11)} - DateTime: {DateTime}";
}
}
Creating the filtered and sorted list becomes much simpler:
string userid = "User1";
var items = new List<Item>();
items.Add(new Book { Id = "B1", Name = "Book1", UserId = "User1", DateTime = new DateTime(2016, 11, 16, 11, 15, 00) });
items.Add(new Book { Id = "B2", Name = "Book2", UserId = "User1", DateTime = new DateTime(2016, 11, 16, 12, 15, 00) });
items.Add(new Book { Id = "B3", Name = "Book3", UserId = "User2", DateTime = new DateTime(2016, 11, 16, 10, 15, 00) });
items.Add(new Comment { Id = "c1", BookId = "B3", UserId = "User1", Content = "cmt1", DateTime = new DateTime(2016, 11, 16, 11, 17, 00) });
items.Add(new Comment { Id = "c2", BookId = "B1", UserId = "User1", Content = "cmt2", DateTime = new DateTime(2016, 11, 16, 11, 16, 00) });
var test = (from b in items
where b.UserId == userid
orderby b.DateTime
select b);
foreach (var o in test)
{
Console.WriteLine(o);
}
Please try this one:
var vm = from b in Book
join c in Comment on b.Id equals c.BookId into d
where b.UserId == userid
orderby b.DateTime
select new {
OrderedDate = from item in d
orderby item.DateTime
select item
}
or another approach by UNION it
var vm = (from a in book
where a.UserId == userid
select new { a.DateTime })
.Union
(from b in Comment
where (x=> x.Book.Any(y=>y.UserId == userid))
select new { b.DateTime })
.Select(c => new { c.DateTime }).OrderBy(d => d.DateTime);
If I understand you correctly, you would like to display the records of two tables with different columns in a single grid.
Which columns are to be displayed?
Lets say they are ID, TYPE (either book entry or comment entry) and DT (DateTime).
var result = (from b in _context.Books
where b.UserId = userid
select new {b.Id Id, "BOOK" Type, b.DateTime DT})
.Union(
(from c in _context.Comments
where c.UserId = userid
select new {c.Id Id, "COMMENT" Type, c.DateTime DT})
).OrderByDescending(x => x.DT);
this is how i did,.
public class BookAndComment
{
public DateTime DateTime { get; set; }
public Book Book { get; set; }
public Comment Comment { get; set; }
}
and query
var orderedBooksAndComments = = books.Where(book => book.UserId == userId).Select(book =>
new BookAndComment
{
DateTime = book.DateTime,
Book = book
}).Union(comments.Where(comment => comment.UserId == userId).Select(comment =>
new BookAndComment
{
DateTime = comment.DateTime,
Comment = comment
}
)).OrderByDescending(bookAndComment => bookAndComment.DateTime).ToList();
than
foreach (var item in orderedBooksAndComments)
{
if(item.Book!=null)
{
// Do something
}else
{
var comment = item.Comment;
// comment should not be null here.
// Do something.
}
}

Seeding the database in Entity Framework

When I try to fill seed in my database I got an error:
The entity found was of type PetsOwner_FF460419E950312585B2229EA7AA6AEAA692190083B7EA5830FB08DEEE79E35D when an entity of type Caretaker was requested."
I went through all tips which I found, I examined the relation between classes and didn't find any solution.
context.CareTakers.AddOrUpdate(c => c.Id,
new Caretaker { Id = 1, Name = "Jan", Surname = "Nowak", Pesel = "29032400352", BankAccountNumber = "05 8202 1016 5582 6188 2074 1719", PhoneNumber = "666-449-292", Accomodations = new List<Accommodation> { context.Accomodations.Find(1) } },
new Caretaker { Id = 2, Name = "Anna", Surname = "Kowalski", Pesel = "29010209388", BankAccountNumber = "76 1610 1335 0163 2600 0322 4138", PhoneNumber = "789-335-709", Accomodations = new List<Accommodation> { context.Accomodations.Find(2) } },
new Caretaker { Id = 3, Name = "Sylwia", Surname = "Boski", Pesel = "42022605090", BankAccountNumber = "21 8499 0008 3163 0216 0506 9685", PhoneNumber = "538 -887-645", Accomodations = new List<Accommodation> { context.Accomodations.Find(3) } },
new Caretaker { Id = 4, Name = "Zbigniew", Surname = "Ryba", Pesel = "10262717572", BankAccountNumber = "61 9614 0008 9550 1997 2431 0213", PhoneNumber = "795-200-495", Accomodations = new List<Accommodation> { context.Accomodations.Find(4) } }
);
context.Accomodations.AddOrUpdate(a => a.Id,
new Accommodation { Id = 1, Beginning = new DateTime(2016, 1, 2), End = new DateTime(2016, 1, 3), PricePerDay = 10.5, Pet = context.Pets.Find(1), Caretaker = context.CareTakers.Find(1) },
new Accommodation { Id = 2, Beginning = new DateTime(2015, 2, 3), End = new DateTime(2016, 2, 4), PricePerDay = 100.0, Pet = context.Pets.Find(2), Caretaker = context.CareTakers.Find(2) },
new Accommodation { Id = 3, Beginning = new DateTime(2014, 9, 10), End = new DateTime(2016, 9, 11), PricePerDay = 12.3, Pet = context.Pets.Find(3), Caretaker = context.CareTakers.Find(3) },
new Accommodation { Id = 4, Beginning = new DateTime(2013, 11, 12), End = new DateTime(2016, 12, 13), PricePerDay = 41.4, Pet = context.Pets.Find(4), Caretaker = context.CareTakers.Find(4) }
);
My classes look like:
public class Accommodation {
public int Id { get; set; }//{BAG}
public virtual Caretaker Caretaker { get; set; }
public virtual Pet Pet { get; set; }
public double PricePerDay { get; set; }
public DateTime Beginning { get; set; }
public DateTime End { get; set; }
public double TotalPrice {
get {
return (End - Beginning).Days * PricePerDay;
}
}
}
[Table("Caretakers")]
public class Caretaker : Person {
public static int Percent => 20;//C# 6
public DateTime ObtainedLicense { get; set; }
public DateTime Hired { get; set; }
public double Salary => 0;
public virtual ICollection<Surgery> Surgeries { get; set; }
public virtual Vet Superior { get; set; }//rekurencyjna
//pochodny
public int AnimalsCurrentlyCareTaken => 0;//Lambda do wyliczenia ilosci opiekowaniymi sie zwierzetyami
public virtual ICollection<Accommodation> Accomodations { get; set; }
}

How to translate t-sql sum() to linq sum()

I have something like this:
And I need to re-write this query into LINQ.
I tried:
var EachProduct = (from b in _context.ReleasePlans
join a in _context.Products
on b.ProductProductId equals a.ProductId
where b.DepartmentDepartmentId == departmentId
select new { ProductId = b, ProductName = a });
But how can I do SUM() and DATEPART() function in Linq?
UPD: Query using lambda expression. But the question remains the same
var EachProduct = _context.ReleasePlans
.Where(b => b.DepartmentDepartmentId == departmentId)
.Join(_context.Products, a => a.ProductProductId, b => b.ProductId,
(b, a) => new {ProductId = b, ProductName = a});
class Program
{
public class ReleasePlan
{
public int ProductProductId { get; set; }
public int Amount { get; set; }
public DateTime DateTime { get; set; }
}
public class Product
{
public int ProductId { get; set; }
public string ProductName { get; set; }
public int DepartmentDepartmentId { get; set; }
}
static void Main()
{
var products = new List<Product>
{
new Product {ProductId = 1, ProductName = "1", DepartmentDepartmentId = 1},
new Product {ProductId = 2, ProductName = "2", DepartmentDepartmentId = 1},
new Product {ProductId = 3, ProductName = "3", DepartmentDepartmentId = 1},
};
var releasePlans = new List<ReleasePlan>
{
new ReleasePlan {ProductProductId = 1, Amount = 1, DateTime = DateTime.Now},
new ReleasePlan {ProductProductId = 1, Amount = 1, DateTime = DateTime.Now},
new ReleasePlan {ProductProductId = 1, Amount = 1, DateTime = DateTime.Now.AddMonths(-5)},
new ReleasePlan {ProductProductId = 2, Amount = 2, DateTime = DateTime.Now},
new ReleasePlan {ProductProductId = 2, Amount = 2, DateTime = DateTime.Now},
new ReleasePlan {ProductProductId = 2, Amount = 2, DateTime = DateTime.Now.AddMonths(-5)},
new ReleasePlan {ProductProductId = 3, Amount = 3, DateTime = DateTime.Now},
new ReleasePlan {ProductProductId = 3, Amount = 3, DateTime = DateTime.Now},
new ReleasePlan {ProductProductId = 3, Amount = 3, DateTime = DateTime.Now.AddMonths(-5)},
};
var amountByProducts = from rp in releasePlans
join p in products
on rp.ProductProductId equals p.ProductId
where p.DepartmentDepartmentId == 1 && (rp.DateTime.AddDays(2).Month/3) == 1
group new {rp, p} by new {rp.ProductProductId, p.ProductId, p.ProductName}
into grp
select new
{
grp.Key.ProductId,
grp.Key.ProductName,
PlannedAmount = grp.Sum(g => g.rp.Amount)
};
Console.WriteLine("ProductId ProductName PlannedAmount");
foreach (var producAmount in amountByProducts)
{
Console.WriteLine("{0} \t\t{1} \t\t{2}", producAmount.ProductId, producAmount.ProductName,
producAmount.PlannedAmount);
}
}
}

How to populate array element defined inside the class

How to populate array element defined inside the class? I would like to Populate List of student with array of Marks,which i am not able to locate .
public class Marks
{
public int ENG { get; set; }
public int MATHS { get; set; }
}
public class Student
{
public string empName { set; get; }
public string empAddress { set; get; }
public Marks[] StudentMarks { set; get; }
}
var objs = new List<Student>()
{
new Employee() {empName = "Manish" empAddress = "MUM"....array element with Marks of two subjects
new Employee() {empName = "Manoj", empAddress = "MUM"....arrayelement with Marks of two subjects
}
In order to initialize array in a single line you should use one of the following code
int[] n1 = new int[4] {2, 4, 6, 8};
int[] n2 = new int[] {2, 4, 6, 8};
int[] n3 = {2, 4, 6, 8};
In your case, you should populate your array with Marks objects.
Marks[] MarksArray = new [] {new Marks() {ENG = 1, MATHS = 1}, new Marks() {ENG = 2, MATHS = 2}};
This array contains 2 marks - new Marks() {ENG = 1, MATHS = 1} and new Marks() {ENG = 2, MATHS = 2}.
You can read find more examples here - https://msdn.microsoft.com/en-us/library/aa287601(v=vs.71).aspx
So you can create your objects this way
var objs = new List<Student>()
{
new Student() {empName = "Manish", empAddress = "MUM", StudentMarks = new [] {new Marks() {ENG = 1, MATHS = 1}, new Marks() {ENG = 2, MATHS = 2}} },
new Student() {empName = "Manish", empAddress = "MUM", StudentMarks = new [] {new Marks() {ENG = 1, MATHS = 1}, new Marks() {ENG = 2, MATHS = 2}} },
};
If you were concerned with the syntax only then this would do:
void Main()
{
var objs = new List<Employee>()
{
new Employee() {
empName = "Manish",
empAddress = "MUM",
StudentMarks = new Marks[] {
new Marks {ENG=10, MATHS = 100},
new Marks { ENG=20, MATHS = 80}, }},
new Employee() {
empName = "Manoj",
empAddress = "MUM",
StudentMarks = new Marks[] {
new Marks { ENG=59, MATHS = 40},
new Marks { ENG=60, MATHS = 80},
new Marks { ENG=80, MATHS = 10},
new Marks { MATHS = 90},
new Marks { ENG=70},}
},
};
foreach (var student in objs)
{
Console.WriteLine("{0} - {1}", student.empName, student.empAddress);
foreach (var mark in student.StudentMarks)
{
Console.WriteLine("Eng:{0}, Maths:{1}", mark.ENG, mark.MATHS);
}
}
}
public class Marks
{
public int ENG { get; set; }
public int MATHS { get; set; }
}
public class Student
{
public string empName { set; get; }
public string empAddress { set; get; }
public Marks[] StudentMarks { set; get; }
}
public class Employee : Student
{ }
However, that is not a good model. A better model would be like this:
void Main()
{
var objs = new List<Student>()
{
new Student() {
Name = "Manish",
Address = "MUM",
StudentMarks = new List<Mark> {
new Mark {Name="Eng", Score = 50},
new Mark {Name="Maths", Score = 60},
}
},
new Student() {
Name = "Manoj",
Address = "MUM",
StudentMarks = new List<Mark> {
new Mark {Name="Maths", Score = 100},
new Mark {Name="Eng", Score = 80},
new Mark {Name="Eng", Score = 70},
new Mark {Name="Eng", Score = 90},
new Mark {Name="Maths", Score = 90},
}
},
};
foreach (var student in objs)
{
Console.WriteLine("{0} - {1}", student.Name, student.Address);
foreach (var mark in student.StudentMarks)
{
Console.WriteLine("Name:{0}, Score:{1}", mark.Name, mark.Score);
}
}
}
public class Mark
{
public string Name { get; set; }
public int Score { get; set; }
}
public class Student
{
public string Name { set; get; }
public string Address { set; get; }
public List<Mark> StudentMarks { set; get; }
}
This one is still not very good. A better one might be:
void Main()
{
var objs = new List<Student>()
{
new Student() {
Name = "Manish",
Address = "MUM",
StudentMarks = new Dictionary<string,List<int>> {
{"Maths", new List<int> {60,70,50}},
{"Eng", new List<int> {80,70,90}},
}
},
new Student() {
Name = "Manoj",
Address = "MUM",
StudentMarks = new Dictionary<string,List<int>> {
{"Maths", new List<int> {70,90}},
{"Eng", new List<int> {40,50,60,60}},
}
},
};
foreach (var student in objs)
{
Console.WriteLine("{0} - {1}", student.Name, student.Address);
foreach (var course in student.StudentMarks)
{
Console.WriteLine("Course:{0}, Average:{1}", course.Key, course.Value.Average());
foreach (var score in course.Value)
{
Console.WriteLine(score);
}
}
}
}
public class Mark
{
public string Name { get; set; }
public int Score { get; set; }
}
public class Student
{
public string Name { set; get; }
public string Address { set; get; }
public Dictionary<string,List<int>> StudentMarks { set; get; }
}
And there are better ones.

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