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.
Related
I have a list that basically look like this...
public class Area
{
public int Id { get; set; }
public string Name { get; set; }
public List<ZipCodeAdresses> ListOfIncludedDestinations { get; set; }
}
public class ZipCodeAdresses
{
public int AreaId { get; set; }
public List<Person> AdressList { get; set; }
}
public class Person
{
public string MottagarNamn { get; set; }
public string Street { get; set; }
}
var intListToRemove = new List<int>(){2,3};
var list = new List<Area>();
var subList = new List<ZipCodeAdresses>();
var personList = new List<Person>
{
new Person() {MottagarNamn = "User 1"},
new Person() {MottagarNamn = "User 2"}
};
subList.Add(new ZipCodeAdresses(){AdressList = personList , AreaId = 1});
personList = new List<Person>
{
new Person() {MottagarNamn = "User 3"},
new Person() {MottagarNamn = "User 4"}
};
subList.Add(new ZipCodeAdresses() { AdressList = personList, AreaId = 2 });
list.Add(new Area(){Name = "List A", ListOfIncludedDestinations = subList});
subList = new List<ZipCodeAdresses>();
personList = new List<Person>
{
new Person() {MottagarNamn = "User 5"},
new Person() {MottagarNamn = "User 6"}
};
subList.Add(new ZipCodeAdresses() { AdressList = personList, AreaId = 3 });
personList = new List<Person>
{
new Person() {MottagarNamn = "User 7"},
new Person() {MottagarNamn = "User 8"}
};
subList.Add(new ZipCodeAdresses() { AdressList = personList, AreaId = 4 });
list.Add(new Area() { Name = "List B", ListOfIncludedDestinations = subList });
I need to be able to remove from the list ListOfIncludedDestinations where AreaId is equal to any integer in intListToRemove which in this example is 2 and 3?
List<T> contains a method RemoveAll, that removes all entries that fulfill a certain condition. In your case it is:
foreach(var entry in list)
{
entry.ListOfIncludedDestinations.RemoveAll(x => intListToRemove.Contains(x.AreaId));
}
This loops through your list, and for every entry it removes all entries in ListOfIncludedDestinations that have an AreadId which is in intListToRemove.
Online demo: https://dotnetfiddle.net/ialnPb
You should add this sample code to remove them from the list :
foreach (var i in list)
i.ListOfIncludedDestinations.RemoveAll(o => intListToRemove.Contains(o.AreaId));
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();
I have the following JSON.
public class Code
{
public string ID { get; set; }
}
public class Country
{
public string CountryID { get; set; }
}
public class Complex
{
public Country Country { get; set; }
public Code Code { get; set; }
public string cText { get; set; }
}
public List<Complex> GetData()
{
List<Complex> Data = new List<Complex>();
Data.Add(new Complex() { Country = new Country() { CountryID = "Australia" }, Code = new Code() { ID = "AU" }, cText = "Australia" });
Data.Add(new Complex() { Country = new Country() { CountryID = "Bermuda" }, Code = new Code() { ID = "BM" }, cText = "Bermuda" });
Data.Add(new Complex() { Country = new Country() { CountryID = "Canada" }, Code = new Code() { ID = "CA" }, cText = "Canada" });
Data.Add(new Complex() { Country = new Country() { CountryID = "France" }, Code = new Code() { ID = "FR" }, cText = "France" });
return Data;
}
I need to get the value of CountryID from the given complex key ("Country.CountryID").
I have tried to get the value using the TryGetValue method in c#. It is doesn't work.
I think I need to split the key and process the Complex JSON and find the nested result.
Could you please suggest how to get the value for the complex object from the given complex key?
It can be done via LINQ like this
var codeIdToFind = "AU";
var result = data.Where(c => c.Code.ID.Equals(codeIdToFind, StringComparison.OrdinalIgnoreCase))
.Select(x => x.Country.CountryID)
.FirstOrDefault();
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();
I have had a look at this Flatten LINQ collection object with nested object collections but it doesn't quite do it for me.
I know there is a lot of code in this post but it's mostly just data to give you the idea of what I'm looking at developing.
if you look at the classes below, I am trying to come up with a way to flatten the result of a search against the file.
So i need to end up with a single flattened record which looks like (the pipes are there to show delimination of a field only)
fileId | FileContact1FirstName | FileContact1LastName | FileContact2FirstName etc | FileClient1FirstName | FileClient1LastName | FileClient1IsNominee | FileClient1IsPrimary | FileClient2FirstName etc....
Any idea on how I can do this without looping through each Contact and Client?
I have these classes of sorts in my edmx;
class File
{
public int fileId { get; set; }
public List<FileContact> fileContacts { get; set; }
public List<FileClient> fileClients { get; set; }
}
class FileContact
{
public Contact contact { get; set; }
}
class FileClient
{
public Contact contact { get; set; }
public bool IsNominee { get; set; }
public bool IsPrimary { get; set; }
}
class Contact
{
public int id { get; set; }
public string firstName { get; set; }
public string lastName { get; set; }
}
And this this as the data simply for testing.
static void FillData()
{
thisFile = new File { fileId = 1, fileContacts = new List<FileContact>(), fileClients = new List<FileClient>() };
thisFile.fileContacts.Add(new FileContact { contact = new Contact { id = 1, firstName = "Andrew", lastName = "Albino" } });
thisFile.fileContacts.Add(new FileContact { contact = new Contact { id = 1, firstName = "Bob", lastName = "Bush" } });
thisFile.fileContacts.Add(new FileContact { contact = new Contact { id = 1, firstName = "Cathy", lastName = "Conti" } });
thisFile.fileContacts.Add(new FileContact { contact = new Contact { id = 1, firstName = "Drew", lastName = "Dram" } });
thisFile.fileContacts.Add(new FileContact { contact = new Contact { id = 1, firstName = "Edward", lastName = "Eliston" } });
thisFile.fileContacts.Add(new FileContact { contact = new Contact { id = 1, firstName = "Frank", lastName = "Fashion" } });
thisFile.fileContacts.Add(new FileContact { contact = new Contact { id = 1, firstName = "Graham", lastName = "Grape" } });
thisFile.fileClients.Add(new FileClient { contact = new Contact { id = 1, firstName = "Harry", lastName = "Who didn't" }, IsNominee = true, IsPrimary = false });
thisFile.fileClients.Add(new FileClient { contact = new Contact { id = 1, firstName = "Indigo", lastName = "Ignacio" }, IsNominee = false, IsPrimary = false });
thisFile.fileClients.Add(new FileClient { contact = new Contact { id = 1, firstName = "Julie", lastName = "Juniper" }, IsNominee = false, IsPrimary = false });
thisFile.fileClients.Add(new FileClient { contact = new Contact { id = 1, firstName = "Kelly", lastName = "Keilor" }, IsNominee = false, IsPrimary = false });
thisFile.fileClients.Add(new FileClient { contact = new Contact { id = 1, firstName = "Liam", lastName = "Loser" }, IsNominee = false, IsPrimary = true });
}
}
This will get you an IEnumerable<string> that contains the properties in the order you specified:
var flattened = new string[] { thisFile.fileId.ToString() }
.Concat(
thisFile.fileContacts
.SelectMany(fc => new string[]
{
fc.contact.firstName,
fc.contact.lastName
}))
.Concat(
thisFile.fileClients
.SelectMany(fc => new string[]
{
fc.contact.firstName,
fc.contact.lastName,
fc.IsNominee.ToString(),
fc.IsPrimary.ToString()
}));
Example: http://ideone.com/Mvc7M
Have a look at SelectMany.