I encountered a problem in my work recently, the following is the source code,
this.NameList = new ObservableCollection<Name>();
List<Others> others1 = new List<Others>();
others1.Add(new Others() { Company = "Company1", School = "School1" });
others1.Add(new Others() { Company = "Company1", School = "School1" });
others1.Add(new Others() { Company = "Company1", School = "School1" });
List<Others> others2 = new List<Others>();
others2.Add(new Others() { Company = "Company2", School = "School2" });
others2.Add(new Others() { Company = "Company2", School = "School2" });
others2.Add(new Others() { Company = "Company2", School = "School2" });
List<Others> others3 = new List<Others>();
others3.Add(new Others() { Company = "Company3", School = "School3" });
others3.Add(new Others() { Company = "Company3", School = "School3" });
others3.Add(new Others() { Company = "Company3", School = "School3" });
this.NameList.Add(new Name() { FirstName = "Jacob", LastName = "Deng", Others = others1 });
this.NameList.Add(new Name() { FirstName = "David", LastName = "Xu", Others = others2 });
this.NameList.Add(new Name() { FirstName = "Helen", LastName = "Liu", Others = others3 });
please click here to see the output of above code
And then please read the following code,
List<Others> others1 = new List<Others>();
others1.Add(new Others() { Company = "Company1", School = "School1" });
others1.Add(new Others() { Company = "Company1", School = "School1" });
others1.Add(new Others() { Company = "Company1", School = "School1" });
List<Others> others2 = new List<Others>();
List<Others> others3 = new List<Others>();
this.NameList.Add(new Name() { FirstName = "Jacob", LastName = "Deng", Others = others1 });
this.NameList.Add(new Name() { FirstName = "David", LastName = "Xu", Others = others2 });
this.NameList.Add(new Name() { FirstName = "Helen", LastName = "Liu", Others = others3 });
Please click here for the output of the second snippet code
From above two snippets, you might notice that the second snippet doesn't contain any items in other2 and other3, you can easily understand from the preview output.
Now the question comes, how do we use LINQ to IEnumerable to handle such case, how to use LINQ to remove those items from Others entity? but we need to keep other2 and other3 not to be null (keep it count is 0). Besides LINQ, are there any other solutions?
I tried to use the following, but failed,
var others = ((MyVM)DataContext).NameList.Select(n => n.Others.Where(o => o.School == "School1")).ToList();
I just did some test, if we don't use LINQ, then we can use the following snippet code to fix my it.
ObservableCollection<Name> nameList = ((MyVM)DataContext).NameList;
foreach(Name n in nameList)
{
List<Others> removeList = new List<Others>();
for(int i=0;i<n.Others.Count;i++)
{
if(n.Others[i].School!="School1")
{
removeList.Add(n.Others[i]);
}
}
foreach(Others other in removeList)
{
n.Others.Remove(other);
}
}
But it looks very redundant, and we know that LINQ is very useful and i believed that it can be fixed with LINQ. Hope someone could help me to fix it with LINQ,thanks.
Appreciate if someone could help me. Thanks a lot.
It's better to use List<T>.RemoveAll in your case. See Using LINQ to remove elements from a List<T> for more details.
ObservableCollection<Name> nameList = ((MyVM)DataContext).NameList;
foreach(Name n in nameList)
{
n.Others.RemoveAll(s => s.School != "School1");
}
Linq is good at read-only iterating instead of updating/deleting. If you insist using Linq, you have to re-construct each item.
var newList = from nl in this.namelist
// this list can be empty but never null
let notSchool1 = (from s in nl.Others
where s.School != "School1"
select s).ToList()
select new Name
{
FirstName = nl.FirstName,
LastName = nl.LastName,
Others = noSchool1
};
this.NameList = new ObservableCollection<Name>(newList);
Above code may break other functionalities in your application, because these items are observed.
As I understand your question,
You want to remove others whose school value is not 'School1'. There are different way available for that but I recommend to filter the data from NameList which you want.
You can try below code to get others list with 'School1'
var result = NameList.Select(name => new Name()
{
FirstName = name.FirstName,
LastName = name.LastName,
Others = name.Others.Where(obj => obj.School == "School1").ToList()
}
).ToList();
Related
Let's say I have a class called Company
public class Company
{
public string name;
public string id;
}
I have a list called CompanyList
var companyList = new List<Company>();
I would like to assign name = "Company A", id = "001" to this empty companyList variable.
How do I do so?
companyList.FirstOrDefault().Name = "Company A";
companyList.FirstOrDefault().Id = "001";
Before you can assign a value to a company in a collection, you must add a company to the collection. You are setting the values of that company, not of the collection itself.
Use either:
var companyList = new List<Company>();
companyList.Add(new Company { Name = "Company A", Id = "001" });
Or:
var companyList = new List<Company>();
companyList.Add(new Company());
// Assumes you want to modify the first company in the list
companyList[0].Name = "Company A";
companyList[0].Id = "001";
You could use collection initializer:
var companyList = new List<Company>{ new Company { Name = "Company A", Id = "001" } };
effectively the same as bellow, a bit more compact
var companyList = new List<Company>();
companyList.Add(new Company { Name = "Company A", Id = "001" });
It seems like you simply need to create an instance of Company and then add it to your list. Here is an example:
List<Company> companyList = new List<Company>();
Company currentCompany = new Company(){id="001", Name="Company A"};
companyList.Add(currentCompany);
Side note ** Be aware of using the code snippet denoted in your question
companyList.FirstOrDefault().Name = "Company A";
companyList.FirstOrDefault().Id = "001";
This could lead to uncaught null reference exceptions, crashing your program. Always check for null when using FirstOrDefault() to instantiate an object.
Company temp = companyList.FirstOrDefault();
if(temp !=null)
{
temp.Name = "Company A";
temp.id = "001";
}
Here is an example where you can initialize the collection directly:
var companyList = new List<Company> {
new Company { name = "Company A", id = "001" },
new Company { name = "Company B", id = "002" }
};
To update the list safetly ( without execption ) you shoud do somthing like this :
var company = companyList.FirstOrDefault() ;
if (company != null) {
company.Name ="aaa";
company.Id = "123" ;
}
Is it possible something like this? I've worked hard, but I think it's not possible, I hope someone will help me.
I have to fill the object like this,
var obj = new
{
parentObj = new List<object>()
{
outsideArray.ForEach(x=>
{
})
}
}
I dont like this.
var obj = new
{
parentObj= new List<object>()
{
new object() { bla, bla }
}
}
I want to do.
var obj {
Id =1,
Name= "any",
Address = new {
userAddressList.forEach(x=> {
Town = x.town,
State = x.state
}
}
}
The code in the second and third code snippets does not comply to C# syntax.
However, you may use LINQ to fill your array nicely similar to what shown in the third snippet:
var obj = new
{
Id = 1,
Name = "any",
Address = userAddressList.Select(x =>
{
Town = x.town,
State = x.state
})
};
Need some help constructing proper LINQ query with join.
I have the following setup:
public class Student
{
public string StudentName { get; set; }
public string StudentEmail { get; set; }
}
public class Enrolled
{
public Student Student { get; set; }
public string Subject { get; set; }
}
public class Dropped
{
public Student Student { get; set; }
public string Subject { get; set; }
}
// Populating the List.
// Setup the Student List
List<Student> lstStudents = new List<Student>();
var John = new Student()
{
StudentEmail = "john#stanford.edu",
StudentName = "John Wayne"
};
var Maya = new Student()
{
StudentEmail = "maya#stanford.edu",
StudentName = "Maya Agnes"
};
var Eric = new Student()
{
StudentEmail = "eric#stanford.edu",
StudentName = "Eric James"
};
var Ellen = new Student()
{
StudentEmail = "ellen#stanford.edu",
StudentName = "Ellen Page"
};
lstStudents.Add(John);
lstStudents.Add(Maya);
lstStudents.Add(Eric);
lstStudents.Add(Ellen);
// Setup the Enrolled List
List<Enrolled> lstEnrolled = new List<Enrolled>();
// John
var JohnMath = new Enrolled() { Student = John, Subject = "Math" };
var JohnScience = new Enrolled() { Student = John, Subject = "Science" };
var JohnEnglish = new Enrolled() { Student = John, Subject = "English" };
// Maya
var MayaMath = new Enrolled() { Student = Maya, Subject = "Math" };
// Eric
var EricMath = new Enrolled() { Student = Eric, Subject = "Math" };
var EricScience = new Enrolled() { Student = Eric, Subject = "Science" };
var EricSocial = new Enrolled() { Student = Eric, Subject = "Social" };
// Ellen
var EllenMath = new Enrolled() { Student = Ellen, Subject = "Math" };
var EllenScience = new Enrolled() { Student = Ellen, Subject = "Science" };
var EllenEnglish = new Enrolled() { Student = Ellen, Subject = "English" };
var EllenSocial = new Enrolled() { Student = Ellen, Subject = "Social" };
lstEnrolled.Add(JohnMath);
lstEnrolled.Add(JohnScience);
lstEnrolled.Add(JohnEnglish);
lstEnrolled.Add(MayaMath);
lstEnrolled.Add(EricMath);
lstEnrolled.Add(EricScience);
lstEnrolled.Add(EricSocial);
lstEnrolled.Add(EllenMath);
lstEnrolled.Add(EllenScience);
lstEnrolled.Add(EllenEnglish);
lstEnrolled.Add(EllenSocial);
// Setup the Dropped List
List<Dropped> lstDropped = new List<Dropped>();
// John dropped Math
var JohnDropMath = new Dropped() { Student = John, Subject = "Math" };
// Eric dropped Social
var EricDropSocial = new Dropped() { Student = Eric, Subject = "Social" };
// Ellen Dropped Math
var EllenDropMath = new Dropped() { Student = Ellen, Subject = "Math" };
lstDropped.Add(JohnDropMath);
lstDropped.Add(EricDropSocial);
lstDropped.Add(EllenDropMath);
What I am trying to achieve is get a list of all students, and the subjects they are enrolled in one line, like:
Student Subjects
------- ----------------------------------
John English, Science
Maya Math
Eric Math, Science
Ellen English, Science, Social
I have constructed the following so far:
var StudentsAndCurrentSubjects = (from st in lstStudents
join en in lstEnrolled
on st.StudentName equals en.Student.StudentName
select new
{
st.StudentName,
en.Subject
})
.ToList();
But it is giving me the result per person per subject.
I hit a wall on how to exclude the dropped items from the list.
I am thinking of traversing the dropped list, like:
foreach(d in lstDropped)
{
// Logic to Remove it from the StudentsAndCurrentSubjects
}
But I feel that it is inefficient (especially if I have lots of rows).
I also do not know how to join the subjects in one line.
Looking for some help here.
Thanks.
We just need to first remove dropped subjects from enrolled list. then group by student.
note that we have list for each student, that it has Student and Subject as its fields, we just select the name of first student from each (as they all the same) and join the subjects together.
var result = lstEnrolled.Where(e => !lstDropped.Any(d => d.Student == e.Student && d.Subject == e.Subject)) //omit dropped courses
.GroupBy(x => x.Student) // group results by students
.Select(x => new {
Name = x.First().Student.StudentName.Split(' ').First(),
Subjects = string.Join(", ", x.Select(e => e.Subject))
}).ToArray();
for printing:
foreach(var x in result)
Console.WriteLine($"{x.Name}\t{x.Subjects}");
LIVE DEMO
You are almost there. The join is correct, you just need to actually apply the grouping you want:
var StudentsAndCurrentSubjects = (from st in lstStudents
join en in lstEnrolled
on st.StudentName equals en.Student.StudentName
select new
{
st.StudentName,
en.Subject
})
.GroupBy(s => s.StudentName, d => d.Subject)
.Select(grp => new { StudentName = grp.Key, Subjects = grp.ToList() })
.ToList();
Then you can use the projection above to display it however you want. Something like this maybe:
foreach (var grouping in StudentsAndCurrentSubjects) {
var studentName = grouping.StudentName;
var subjects = string.Join(", ", grouping.Subjects);
Console.WriteLine($"{studentName}\t{subjects}");
}
Try a GroupJoin by using the into keyword.
var studentsAndCurrentSubjects = (
from student in lstStudents
join enrollment in lstEnrolled
on student equals enrollment.Student
into enrollments
join drop in lstDrop
on student equals drop.Student
into drops
let classes = enrollments.Select(e => e.Subject).Except(drops.Select(d => d.Subject))
select new
{
Student = student.StudentName,
Subjects = string.Join(", ", classes)
})
.ToList();
Or, you can subquery with let. This will have noticeably slower performance against larger lists than the other solution, but it should be fine in the database.
var studentsAndCurrentSubjects = (
from student in lstStudents
let enrollments = lstEnrolled.Where(x => student == x.Student).Select(x => x.Subject)
let drops = lstDrops.Where(x => student == drop.Student).Select(x => x.Subject)
let classes = enrollments.Except(drops)
select new
{
Student = student.StudentName,
Subjects = string.Join(", ", classes)
})
.ToList();
I have a Collection of Data and the Dictionary:
Collection handle Student Data
Dictionary Keeps Student Courses.
I want to get the Course Name from the Dictionary and put it into as CourseName.
private viod GenerateStudentDetails(Student studentData)
{
var courses = m_courses.GetCoursesDictionary(); // returns Dictionary<Guid,string>()
var studentDetails= from data in studentData
select new
{
FirstName = data.FirstName,
LastName = data.LastName,
Email = data.Email,
Mobile = data.Profile.Mobile,
City = data.Profile.City,
PostCode = data.Profile.PostCode,
CourseName = courses[data.Profile.CourseID ?? Guid.Empty]
};
}
"LINQ to Entities does not recognize the method 'System.String get_Item(System.Guid)' method, and this method cannot be translated into a store expression."
You could try something as the below:
private viod GenerateStudentDetails(Student studentData)
{
var courses = m_courses.GetCoursesDictionary(); // returns Dictionary<Guid,string>()
var studentDetails= (from data in studentData
select new
{
FirstName = data.FirstName,
LastName = data.LastName,
Email = data.Email,
Mobile = data.Profile.Mobile,
City = data.Profile.City,
PostCode = data.Profile.PostCode,
CourseID = data.Profile.CourseID
}).AsEnumerable()
.Select(item=>new
{
FirstName = item.FirstName,
LastName = item.LastName,
Email = item.Email,
Mobile = item.Profile.Mobile,
City = item.Profile.City,
PostCode = item.Profile.PostCode,
CourseName = courses[item.Profile.CourseID ?? Guid.Empty]
});
}
What's the problem?
The problem is that the last expression in the anonymous type you create,
CourseName = courses[data.Profile.CourseID ?? Guid.Empty]
cannot be in this place, because it can't be translated appropriately. So you have this option. You can declare a sequence of the data you want from the studentData and then make any conversion of call anything you want to the new anonymous type we create.
Just for thought
var eCourses = ((IEnumerable<KeyValuePair<Guid, string>>) courses);
var studentDetails = (from data in studentData
select new
{
data.FirstName,
data.LastName,
data.Email,
data.Profile.Mobile,
data.Profile.City,
data.Profile.PostCode,
CourseName = eCourses.FirstOrDefault(s => s.Key == data.Profile.CourseID).Value
});
I have a string with a message containing some fields I want to swap out to actual values
var message = "Hi [CustomerName]. Its [TODAY], nice to see you were born on the [DOB]!";
var mappingCodes = new List<string> {"[CUSTOMER_NAME]","[DOB]",[TODAY]};
var customEmails = new Dictionary<string, string>();
var today = DateTime.Now;
var customers = new List<Customer>()
{
new Customer()
{
FirstName = "Jo",
LastName = "Bloggs",
Email = "jo#bloggs.com",
DOB = "10/12/1960"
}
};
foreach (var customer in customers)
{
var emailMessage = "";
customEmails.Add(customer.Email,emailMessage);
}
What I'm trying to do is loop through each of the customers and take the message replacing any of the mappingCodes with actual codes.
e.g. [Today] Would be the today and CustomerName would be Customer.FirstName + Customer.LastName
There could be 1000's of customers so I need something robust. I'm just not sure how to first check the string contains any of the mappingCodes and then replace them with the desired values.
Any advice?
You could try something like this. String.Format is rather efficient. It also would allow you to format Date.Today, if you want.
var customers = new List<Customer>()
{
new Customer()
{
FirstName = "Jo",
LastName = "Bloggs",
Email = "jo#bloggs.com",
DOB = "10/12/1960"
}
};
foreach (var customer in customers)
{
var emailMessage = String.Format("Hi {0}. Its {1}, nice to see you were born on the {2}!", customer.FirstName, DateTime.Today, customer.DOB);
customEmails.Add(customer.Email,emailMessage);
}
You can use Regex.Replace(string, MatchEvaluator):
var customers = new[] {
new {
Name = "Fred Flintstone",
City = "Bedrock"
},
new {
Name = "George Jetson",
City = "Orbit City"
}
};
string template = "Hello, [NAME] from [CITY]!";
var re = new Regex(#"\[\w+\]"); // look for all "words" in square brackets
foreach (var customer in customers)
{
Trace.WriteLine(
re.Replace(template, m => {
// determine the replacement string
switch (m.Value) // m.Value is the substring that matches the RE.
{
// Handle getting and formatting the properties here
case "[NAME]":
return customer.Name;
case "[CITY]":
return customer.City;
default:
// The "mapping code" is not supported, I just return the
// original substring
return m.Value;
}
}));
}
Obviously the above is just the general approach, you'll have to modify it to support your mapping codes and data structures.