convert string in an array of objects - c#

I have two different classes: Employee and Customer. Each have two properties in common: Name and Address. Is there a way convert the string directly into an array of objects without using the List<>?
private static List<Employee> NewMethod1(string strArr)
{
List<Employee> lst = new List<Employee>();
if (strArr !=null)
{
strArr.Split(',').ToList().ForEach(x => lst.Add(new Employee() { Name = x }));
}
return lst.ToArray();
}
or make this line of code generic enough so I can use it inline code?
strArr.Split(',').ToList().ForEach(x => lst.Add(new Employee() { Name = x }));

as #canton7 said in comments you can use Linq:
strArr?.Split(',').Select(x => new Employee() {...}).ToList() ?? new List<Employee>()

Related

filter by list in an attribute that is also a list C#

I have a class named SchoolClass, which has a list of students as attribute.
Then I have a search filter, in which I can search by a list of students and I have to return all the SchoolClasses where those students are part of their "list of students".
public class SchoolClass
{
public List<string> students { get; set; }
}
List<string> searchedStudents = new List<string>
{ "Brian","Adrian","Matt","Chloe"};
So I have a list of SchoolClasses:
List<SchoolClass> schoolClasses = new List<SchoolClass>();
SchoolClass 1 ==>
//(it should return it because it matches Brian, one of the searchedStudents)
schoolClasses[0].students = { "Brian","Zara"};
SchoolClass 2 ==>
//(it shouldn't return it because there are no matches)
schoolClasses[1].students = { "Sophie","Zara"};
i assume this is some sort of school work / project.
please make sure that you actually understand the answer and learn some sort of lesson out of it!
since this isnt a very data intense operation (with billions of entries) we can simply use go over the lists and search for the students.
if you got huge amounts of data to go through, you should consider different data structures (eg. hashtables).
//polulate the list with data
SchoolClass schoolClass1 = new SchoolClass();
SchoolClass schoolClass2 = new SchoolClass();
SchoolClass schoolClass3 = new SchoolClass();
schoolClass1.students = new List<string> { "Brian", "Adrian", "Matt" };
schoolClass2.students = new List<string> { "Adrian", "Matt" };
schoolClass3.students = new List<string> { "Brian", "Matt", "Chloe" };
List<SchoolClass> schoolClasses = new List<SchoolClass>();
schoolClasses.Add(schoolClass1);
schoolClasses.Add(schoolClass2);
schoolClasses.Add(schoolClass3);
//set our filter
List<string> searchedStudents = new List<string> { "Brian", "Chloe" };
//filter the data by going over the lists
List<SchoolClass> classesWithSearchedStudents = new List<SchoolClass>();
for (int classIterator = 0; classIterator < schoolClasses.Count(); classIterator++)
{
for (int filterIterator = 0; filterIterator < searchedStudents.Count(); filterIterator++)
{
//comparing with our filter and add class to the results list
if (schoolClasses[classIterator].students.Contains(searchedStudents[filterIterator]))
{
classesWithSearchedStudents.Add(schoolClasses[classIterator]);
break;
}
}
}
printResult(classesWithSearchedStudents);
just for demonstration, here is a more compact version using linq
var searchResult = new List<SchoolClass>();
foreach (var student in searchedStudents)
{
searchResult.AddRange(schoolClasses.Where(x => x.students.Contains(student)));
}
searchResult = searchResult.Distinct().ToList();
printResult(searchResult);
for printing the answer, i used this function:
private static void printResult(List<SchoolClass> schoolClasses)
{
for (int classIterator = 0; classIterator < schoolClasses.Count(); classIterator++)
{
Console.Write("found class with students: ");
for (int studentIterator = 0; studentIterator < schoolClasses[classIterator].students.Count; studentIterator++)
{
Console.Write(schoolClasses[classIterator].students[studentIterator] + ", ");
}
Console.WriteLine();
}
}
output:
found class with students: Brian, Adrian, Matt,
found class withstudents: Brian, Matt, Chloe,
If I understand it correctly:
You want to filter all schoolclasses that have "any" student from the filterlist in their student list.
If that assumption is correct than here you go.
Here's a simpeler and shorter function you can use.
It has one less for loop and cuts off whenever it finds 1 of the students.
It looks through all classes and sees if "any" of the filtered students are in there".
public IEnumerable<SchoolClass> FindMatches(List<SchoolClass> schoolClasses, List<string> namesFilter)
{
return schoolClasses.FindAll(schoolClass => schoolClass.students.Any(student => namesFilter.Contains(student)));
}
Obviously you can get the single line out of the method, but I thought I post it like this so you have an idea of the inputs and what the variable names actually are.
An easy way to check whether two lists contain any identical values is to use .Intersect() and .Any() (both methods are found in the System.Linq namespace).
.Intersect() returns an IEnumerable of all distinct objects that are found in both lists.
By using listA.Intersect(listB).Any(), you basically get the answer to the question
Are there any items that exists in both listA and listB?
In your example, the intersection of each school class's students with the searched students would result in the following (pseudo code):
School class 1
    schoolClasses[0].Students .Intersect( searchedStudents )
= { "Brian", "Zara" } .Intersect( { "Brian", "Adrian", "Matt", "Chloe" } )
= { "Brian" }
"Brian" is the only student that is present in both lists.
School class 2
    schoolClasses[1].Students .Intersect( searchedStudents )
= { "Sophie", "Zara" } .Intersect( { "Brian", "Adrian", "Matt", "Chloe" } )
= { }
No student is present in both lists.
Now, let's imagine school class 3 (schoolClasses[2]) existed and had the following students:
{ "Stuart", "Chloe", "Adrian", "Maya", "Chloe" }
An intersection of school class 3's students and the searched students would result in:
School class 3
    schoolClasses[2].Students .Intersect( searchedStudents )
= { "Stuart", "Chloe", "Adrian", "Maya", "Chloe" } .Intersect( { "Brian", "Adrian", "Matt", "Chloe" } )
= { "Chloe", "Adrian" }
Both "Chloe" and "Adrian" are present in both lists.
Note how "Chloe" is present twice in the school class's student list, but only once in the result from the intersection. Seeing as your need simply is to know whether a student name is present in both lists, (and not how many times a student name is present in either list,) .Intersect() nonetheless suits your use case.
So, to the implementation.
If your SchoolClass class looks like this:
public class SchoolClass
{
public List<string> Students { get; set; }
}
and you define searchedStudents and schoolClasses as follows:
List<string> searchedStudents = new() { "Brian", "Adrian", "Matt", "Chloe" };
List<SchoolClass> schoolClasses = new()
{
new() { Students = new() { "Brian", "Zara" } },
new() { Students = new() { "Sophie", "Zara" } },
};
, the intersection may be implemented as follows, using a foreach loop:
var matchingSchoolClasses = new List<SchoolClass>();
foreach (var schoolClass in schoolClasses)
{
if (schoolClass.Students.Intersect(searchedStudents).Any())
{
matchingSchoolClasses.Add(schoolClass);
}
}
or as follows, using .Where(), also from the System.Linq namespace:
var matchingSchoolClasses = schoolClasses
.Where(schoolClass => schoolClass.Students.Intersect(searchedStudents).Any())
.ToList();
Either implementation will result in matchingSchoolClasses containing one object. That one object is school class 1 (schoolClasses[0]), which contains the following students:
{ "Brian", "Zara" }
Example fiddle here.

Convert a List of Anonymous type to a list of a specific Class type

I have an abstract class "Employee".
I have a factory method that extends on this Employee class to query the database to return a list of all active employees:-
public static class EmployeeExtentions
{
public static List<Employee> FireEmployees(this List<Employee> AllCitiesEmps)
{
List<Employee> employees = new List<Employee>();
using (var ctx = new hr_employeeEntities())
{
var emp = (from x in ctx.F_Emp
join y in ctx.HR_EMPLon (x.employee_id).ToString() equals y.EMPLID
where x.employment_status == "A"
select new { x, y }).ToList();
// emp.ForEach( x => { employees.Add(new Employee(employees)); });
//var emps = emp.Select(x => PropertyCopy<Employee>.CopyFrom(x)).ToList();
//emp.ForEach(x => { employees.Add(new Employee(x)); });
}
return employees;
}
}
The var 'emp' is a list of all active employees but an anonymous list. I want to convert this into a strongly typed List of type Employee. I have 3 commented statements in my code which were my attempts.
What is the relationship between F_Emp and HR_EmpLon? It seems that these are loosely coupled through Employee_Id / EMPID depending on the table, but which table represents "Employee"?
Firstly: This does not look like it needs to be an extension method. Extension methods are meant for creating a method that will apply to a given instance of a variable. In this case "AllCitiesEmps" you are not using this instance, so at a minimum this could just be a Static method on Employee itself. (Frankly though, better served as a Repository method)
If Employee is mapped to F_Emp then the join is unnecessary:
public static List<Employee> FireEmployees()
{
using (var context = new hr_employeeEntities())
{
var employees = context.F_Emp
.Where(x => x.employment_status == "A")
.ToList();
return employees;
}
}
If Employee maps to the HR_EmpLon table and these tables do not share a common FK between them: (Disclaimer, this is a stab from memory, so it may need some tweaking. I rarely ever need to use explicit joins.)
public static List<Employee> FireEmployees()
{
using (var context = new hr_employeeEntities())
{
var employees = context.HR_EMPLon
.Join(context.F_Emp,
h => h.EMPLID,
e => e.employee_id.ToString(),
(h, e) => new {HREmployee = h, FEmployee = e})
.Where(x => x.FEmployee.employment_status == "A")
.Select(x => x.HREmployee)
.ToList();
return employees;
}
}
If an Employee is not an entity mapped to either table, but represents a mix of data from these two tables, then I would recommend setting up a View in your database to join this data, and map your entity to the view.
You can try something like this:
public static class EmployeeExtentions
{
public static List<Employee> FireEmployees(this List<Employee> AllCitiesEmps)
{
List<Employee> employees = new List<Employee>();
using (var ctx = new hr_employeeEntities())
{
var emp = (from x in ctx.F_Emp
join y in ctx.HR_EMPL on (x.employee_id).ToString() equals y.EMPLID
where x.employment_status == "A"
select new
{
x.employee_id,
x.employment_status,
//x.OtherProperties
y.EMPLID,
//y.OtherProperties
}).ToList();
employees = emp.Select(x => (new EmployeeDerived { EmployeeId = x.employee_id, EmploymentStatus = x.employment_status }) as Employee).ToList();
}
return employees;
}
private class EmployeeDerived : Employee
{
}
}
Please note, you will need to create a new derived type though, as you can't directly cast to an abstract type.

Use of SelectMany

Here in below code we can show difference between Select and SelectMany operator.
Is there any way to avoid the common skills? For example if two employees have the C# skill then I want to print them only once.
namespace LinqOperators
{
class Employee
{
public string Name { get; set; }
public List<string> Skills { get; set; }
}
class Program
{
static void Main(string[] args)
{
List<Employee> employees = new List<Employee>();
Employee emp1 = new Employee { Name = "Deepak", Skills = new List<string> { "C", "C++", "Java" } };//Adding Skills List to Employee List i.e List of List
Employee emp2 = new Employee { Name = "Karan", Skills = new List<string> { "SQL Server", "C#", "ASP.NET" } };
Employee emp3 = new Employee { Name = "Lalit", Skills = new List<string> { "C#", "ASP.NET MVC", "Windows Azure", "SQL Server" } };
employees.Add(emp1);
employees.Add(emp2);
employees.Add(emp3);
// Query using Select()
IEnumerable<List<String>> resultSelect = employees.Select(e => e.Skills);
Console.WriteLine("**************** Select ******************");
// Two foreach loops are required to iterate through the results
// because the query returns a collection of arrays.
foreach (List<String> skillList in resultSelect)
{
foreach (string skill in skillList)
{
Console.WriteLine(skill);
}
Console.WriteLine();//To differntiate Two Skill Lists
}
// Query using SelectMany()
IEnumerable<string> resultSelectMany = employees.SelectMany(emp => emp.Skills);
Console.WriteLine("**************** SelectMany ******************");
// Only one foreach loop is required to iterate through the results
// since query returns a one-dimensional collection.
foreach (string skill in resultSelectMany)
{
Console.WriteLine(skill);
}
Console.ReadKey();
}
}
}
SelectMany will flatten your IEnumerable such that it won't produce IEnumerable of IEnumerables but IEnumerable:
IEnumerable<IEnumerable<string>> skills; //not this [[C#, Java], [C, C++, Java, C#]]
IEnumerable<string> skills; //but this [C#, Java, C, C++, Java, C#]
You could use Distinct in your resultSelectMany to get common skill only once.
resultSelectMany = resultSelectMany.Distinct(); //[C#, Java, C, C++]
Or to put it in the same line:
// Query using SelectMany()
IEnumerable<string> resultSelectMany = employees.SelectMany(emp => emp.Skills).Distinct();
You can use .Distinct() to remove duplicates

C# LINQ get objects with field value that do not match a string array blacklist

I have the following code:
public class MyObject {
public string Name { get; set; }
public MyObject(string name) {
Name = name;
}
}
// caller method somewhere
public void myMethod() {
List<MyObject> myObjects = new List<MyObject>();
myObjects.Add(new MyObject("Jim"));
myObjects.Add(new MyObject("David"));
myObjects.Add(new MyObject("Richard"));
myObjects.Add(new MyObject("Steve"));
string[] namesToExclude = new string[] { "Jim", "Steve" };
List<string> strings = myObjects.Select(m => m.Name).Except(namesToExclude).ToList();
// replace the above line to result in List<MyObject> with 2 items (David, Richard)
}
I'd like to get this last line working so I can get back the list as a List<MyObject> rather than as a List<string>.
Try this:
var objects =
myObjects.Where(m => !namesToExclude.Contains(m.Name)).ToList();
You can use Except and join the result to the original list
var allowedObjNames = myObjects.Select(o => o.Name).Except(namesToExclude);
var allowedObject = from o in myObjects join a in allowedObjNames
on o.Name equals a
select o;
var allowedObjList = allowedObject.ToList();
var objs = myObjects.Where(m => !namesToExclude.Contains(m.Name)).ToList();
Replace last line with -
List<MyObject> objects = myObjects.Where(o =>
!namesToExclude.Contains(o.Name)).ToList();
this may be slightly wrong as I'm used to using VB.NET more, but something like...
(from m in myObjects where !namesToExclude.Contains(m.Name)).ToList()

Linq Count Expression

Suppose the following Employee class (yes I know I shouldn't publicly expose Lists but this is just an example):
class Employee
{
public string Name {get; set;}
public List<string> Skills {get; set;}
}
Skills is just a list of skills the employee has, for example "programming", "customer service", etc.
Now suppose I have a List<Employee> CurrentEmployees, and I also have another employee, Employee NewHire that is not in the CurrentEmployees list.
My goal is to use a lambda expression to count how many employees in CurrentEmployees have at least one skill that NewHire also has. So for example, if CurrentEmployees contains one employee with Skills{'Programming', 'Troubleshooting'}, and another employee with Skills{'Accounting','Finance'}, and NewHire has Skills{'Programming','Networking'}, I would want an expression that returns 1, because the first employee in the list also has 'Programming' as a skill... is this possible?
Thanks for any help.
currentEmployees.Count(ce =>
ce.Skills.Intersect(newHire.Skills).Any())
var currentEmployees = new List<Employee>
{
new Employee { Skills = new List<string> { "Programming", "Troubleshooting" } },
new Employee { Skills = new List<string> { "Accounting", "Finance" } },
};
var newHire = new Employee { Skills = new List<string> { "Programming", "Networking" } };
var count = currentEmployees.Count(e => e.Skills.Any(newHire.Skills.Contains));
// count == 1
If performance was important, I would use a HashSet<string>:
var newHireSkills = new HashSet<string>(newHire.Skills);
var count = currentEmployees.Count(e => e.Skills.Any(s => newHireSkills.Contains(s)));

Categories

Resources