Better way for Comparing List in c# - c#

I have two list say
List<string> names; and List<Student> stud;
Student Class has 3 properties
ID
Name
Section
Now i want to loop through List<string> and compare each item with Name property in List<Student> and want to perform operations if they are not equal
I tried looping through names and comparing each values to stud.
But i thought there must be some better way of doing this with LINQ or should i be using YIELD.
Thanks

It's not very clear from your description, but if you want "all students whose names aren't in the list" you can definitely use LINQ:
var studentsWithoutListedNames = stud.Where(s => !names.Contains(s.Name));
foreach (var student in studentsWithoutListedNames)
{
// Whatever...
}

If your intention is not what Jon describes but more to compare the list of names with the list of student names and find differences:
var invalidStudents = names.Zip(stud, (name, student) => new {name, student}).
Where(item => (item.name != item.student.Name));
if (invalidStudents.Any()) // Or foreach...
{
...
}
for example:
var names = new string[] { "John", "Mary" };
var stud = new Student[] { new Student(1, "John", "IT"), new Student(2, "Jack", "Math") };
var invalidStudents = names.Zip(stud, (name, student) => new {name, student}).
Where(item => (item.name != item.student.Name));
foreach (var item in invalidStudents)
{
Console.WriteLine(item.name);
}
Should write Mary

Another good way of doing this would be:
var notOnList = students.Except(from student in students
join name in names on student.Name equals name
select student);
foreach(var student in notOnList)
{
...
}

Related

C# Lambda to filter list based on existence on another list

List A: 3,5,5,5,7,9
List B: 3,5
Both of the list are the same type and those values are from a field ID. My objective is to construct a forloop that will return me 7,9 because 7,9 is not existed in List B.
I've tried the following but no luck:
int counter = 0;
foreach(var item in ListA.Where(x=>ListB.Any(b=>x.ID != b.ID)))
{
counter++;
//Here I should perform operation with item that having ID 7 and 9
}
Updates:
Using a except method in the above case, counter will still return me 4 simply because each of the 5 in ListA are different object eventhou they are sharing the same ID. My ultimate objective is to have the counter as 2 irregardless whether the object is the same or not. As long as the ID of object in ListA is 3 or 5, I would wanna exclude it.
Just use the Except extension mtehod
foreach (var item in ListA.Except(ListB)) {
...
}
it should be "ALL", or "Not Any"
foreach(var item in ListA.Where(x=>ListB.All(b=>x.ID != b.ID)))
{
//Here I should perform operation with item that having ID 7 and 9
}
update:
As you actually want to have distinct result from A except B, so, you can do either:
foreach(var item in ListA.GroupBy(m=>m.ID).Where(x=>ListB.All(b=>b.ID != x.Key)))
{
counter ++;
Debug.writeline(item.Key);
}
or
foreach(var id in ListA.Select(x=>x.ID).Distinct().Except(ListB.Select(y=>y.ID)))
{
counter++;
}
note: all untested - i have no compiler with me for the moment.
Change your query like this:
foreach(var item in ListA.Where(x=> !ListB.Any(b => x.ID == b.ID)))
And it should work fine.
Try This:
List<int> listA=new List<int>(new[]{ 3,5,7,9});
List<int> listB=new List<int>(new[]{ 3,5});
var items=(from a in listA
select a).Except(from b in listB
select b);
foreach(var item in items)
{
Console.WriteLine(ll);
}
Output:
7
9
Except method can be used when both List are of same type.
If Type is different. We can use like this.
var outPut = _employees.Where(i => _employeeExtensions.Any(j => i.EmpId == j.EmpId));
I think you want to get the items in a list where the items' IDs are different:
Example that I put together in LinqPad:
void Main()
{
List<Person> a = new List<Person>()
{
new Person { ID = 1 },
new Person { ID = 2 },
new Person { ID = 3 },
};
List<Person> b = new List<Person>()
{
new Person { ID = 1 },
};
var c = a.Where(x => b.Any(bprime => bprime.ID != x.ID));
foreach(var item in c)
{
Console.WriteLine(item.ID);
}
}
class Person
{
public int ID { get; set; }
}
Output:
2
3
This works similar to the Except method but this will check the elements' properties.

LINQ look up list of objs in datable of different objs that share a common property

I have list A which is obj A and list B which is obj B. Both list share one property and I want to look up all the obj B of list B has in A and pull them out.
So, ex.
List A is a bunch of people
List B is a bunch of names
Both list have a personId
Now I want to get all the people with the names that are in List B. I was thinking something like a:
class names
{
public int id {get;set;}
public string name {get;set;}
}
class people
{
public int id {get;set;}
public string name {get;set;}
}
var newList = new List<person>();
foreach(var n in names)
{
var person = people.firstordefault(p => p.name == n);
if(person!=null)
{
newList.Add(person);
}
}
}
I was wondering is there is a more efficent way with LINQ I can do this because it wont be a list everytime it might be the database im calling it from and i dont want to call the database a thousands for no reason.
This is probably a bad example if i think about it.
This codes :
var newList = new List<person>();
foreach(var n in names)
{
var person = people.firstordefault(p => p.name == n);
if(person!=null)
{
newList.Add(person);
}
}
will produce the same result as :
var newList = new List<person>();
newList = people.Where(p => names.Contains(p.name)).ToList();
responding your update, if names is a list of names object instead of string, you can do as follow :
newList = people.Where(p => names.Select(o => o.name).Contains(p.name)).ToList();
With LINQ, You can do this:
var intersection = ListA.Intersect(ListB);
However, this is the set intersection, meaning if ListA and ListB don't have unique values in it, you won't get any copies. In other words if you have the following:
var ListA = new [] { 0, 0, 1, 2, 3 };
var ListB = new [] { 0, 0, 0, 2 };
Then ListA.Intersect(ListB) produces:
{ 0, 2 }
If you're expecting:
{ 0, 0, 2 }
Then you're going to have to maintain a count of the items yourself and yield/decrement as you scan the two lists.
Since you're dealing with two different classes, what you're really looking for is a join.
List<Person> people = new List<Person>{new Person{Name = "Mark"},
new Person{Name = "Alice"},
new Person{Name = "Jane"}};
List<string> names = new List<string>{"Mark"};
var query = from p in people
join n in names on p.Name equals n
select p; // will output Person Mark
Note: This has time complexity of O(p+n) (where p = number of people and n = number of names), because join is implemented as a hash join. Your nested loop above or a Where/Contains LINQ query time complexity O(p*n), since it's iterating n for every p. This may or may not be an issue depending on the sizes of your collections.

How to initialize var outside foreach

I want to initialize var outside the foreach loop.
Here is my code:
public List<Course> GetCourse()
{
IList<Semester> semesters = Semester.Get();
foreach (Semester sm in semesters)
{
IList<CourseInstance> courseInstances = CourseInstance.Get(sm[0].SemesterId);
var courseInfos = from c in courseInstances
select new Course { Code = c.Course.Code, Name = c.Course.Name };
}
return courseInfos.ToList();
}
How do I initialize courseInfos out side the foreach loop? I try to initialize with null give me error!
var infers the type from the value you are initialising with, so initialising with null will never work. Everything else will.
I believe the Linq statement you want is
var courses = semesters
.SelectMany( s => CourseInstance.Get(s.SemesterId)
.Select( c => new Course ( Code = c.Course.Code, Name = c.Course.Name ) )
.ToList();
EDIT:
If you want to map SemesterName to a list of courses, I would recommend a dictionary.
semesters.ToDictionary(semester => semester.Name, semester =>
semesters.SelectMany(sid =>
CourseInstance.Get(sid.SemesterId))
.Select(c => new Course
{Code = c.Course.Code, Name = c.Course.Name}).ToList())
This will create a Dictionary<string, List<Course> This is nearly identical to the code below, except that it maps the semester.Name as the key. This would, of course, mean you have to have unique semester names, otherwise the dictionary can't be created.
You are reinitializing courseInfos every time you loop in the foreach, so you will only get a list of the last semesterId.
You can write a linq query that does this all in one line for you.
return semesters.SelectMany(sid => CourseInstance.Get(sid.SemesterId))
.Select(c => new Course { Code = c.Course.Code,
Name = c.Course.Name }).ToList()
To break it down,
.SelectMany(sid => CourseInstance.Get(sid.SemesterId))
does the same thing as the foreach. It will return an IEnumerable<CourseInstance>.
After that, you are calling
.Select(c => new Course { Code = c.Course.Code, Name = c.Course.Name })
on the result that we got in the last section; it returns an IEnumerable<Course> that you turn into a list.
SelectMany works similar to Select except it will take each IEnumerable<Course> and flatten it into one sequence instead of IEnumerable<IEnumerable<Course>>
The answer is:
IEnumerable<Course> courseInfos = null;
foreach (Semester sm in semesters)
{
IList<CourseInstance> courseInstances = CourseInstance.Get(semesters[0].SemesterId);
courseInfos = from c in courseInstances
select new Course { Code = c.Course.Code, Name = c.Course.Name };
}
return courseInfos.ToList();
However, you are discarding everything but the final iteration of the foreach. Is this what you meant to do?
why not just initialize the first instance of courseInfo with the first semester and then iterate over Semesters, is there a reason you need to initialize courseInfo before the foreeach?
may be this help. Collecting course info in each iteration.
public List<Course> GetCourse()
{
IList<Semester> semesters = Semester.Get();
List<Course> courseInfos = new List<Course>();
foreach (Semester sm in semesters)
{
IList<CourseInstance> courseInstances = CourseInstance.Get(sm.SemesterId);
IEnumerable<Course> result = from c in courseInstances
select new Course { Code = c.Course.Code , Name = c.Course.Name };
courseInfos.AddRange(result);
}
return courseInfos;
}

LINQ, Combine Items with a similar value

Assuming I had a collection like this..
var list = new List<Item>
{
new Item
{
Name = "Software",
Price = 100
},
new Item
{
Name = "Software",
Price = 200
},
new Item
{
Name = "Hardware",
Price = 100
}
};
And the 'Names' are not going to be known, I want to write a LINQ query that will return a list of everything with a matching name. I cannot use "Select", because the names, again, are not known at design time.
Any ideas?
I'm not entirely sure whether you want to filter, or group the results.
If you want to filter, you can use Where with a runtime-supplied name:
string nameToFind = GetTheNameToFind(); // Can come from user, config, etc
var matches = list.Where(item => item.Name == nameToFind);
If you want to group by all of the names (ie: have 2 software + 1 hardware element), you could use Enumerable.GroupBy:
var groups = list.GroupBy(item => item.Name);
foreach(var group in groups)
{
Console.WriteLine("Group {0}:", group.Key);
foreach(var item in group)
Console.WriteLine(" Item: {0} - {1}", item.Name, item.Price);
}
Are you looking for this? You can use Where method to filter enumerable.
name variable can be defined at runtime.
string name = "Software";
List<Item> newList = list.Where(item => item.Name == name).ToList();

Looking to group specific items from a list based on names using linq, html helper related

I have a list of items I am retrieving which i wish to be grouped into divs
depending on the common name that a set of the list items may have
say for instance a list of firstnames I have.
i would like to be able to create a div dynamically based on the items common attibutes.
id 23
fistname darren
id 37
fistname darren
id 67
fistname darren
id like to group all the firstnames darren into one div and any others that share common firstnames
cheers
I don't know about htmlhelper, but the linq groupby method is simple to use:
var firstnamegroups = items.GroupBy(item => item.FirstName);
I'll help you with the query only, you can continue after this point with the your HTML code.
Sample Data:
List<Person> list = new List<Person>();
list.Add(new Person() { FirstName = "A", ID = 1 });
list.Add(new Person() { FirstName = "B", ID = 2 });
list.Add(new Person() { FirstName = "B", ID = 3 });
list.Add(new Person() { FirstName = "C", ID = 4 });
The LINQ expression:
var result = list.GroupBy(item => item.FirstName);
Put here your HTML code:
foreach (var item in result)
{
string name = item.Key;
// Add here the div and use [name]
foreach (var person in item)
;// Add here the items in the div
}
Good luck!

Categories

Resources