user3164605
user3164605

Reputation: 1

Linq based filtering for the class based on the list values

var filteredStudent = new List<Student>();
string description = "Managing the Course";
foreach (var student in students)
{
    if (student.subjetcs.Any(x => x.Sylabus.Name.Contains("Critical")))
    {
        if (student.subjetcs.Any(x => x.Sylabus.Name.Contains("Critical") &&
                                      description.Contains(x.Description)))
        {
           filteredStudent.Add(student);
        }
    }
    else
    {
        filteredStudent.Add(student);
    }
}

Is it possible to replace all the above logic with single Linq statement?

As I am interested in the Linq a lot

Upvotes: 0

Views: 36

Answers (3)

James Curran
James Curran

Reputation: 103575

First step: Simplify your logic. And first step there, get rid of the redundant check for Name.Contains("Critical"). By the time you've reached there, you are absolutely certain that Name contains "Critical".

So the criteria is -- IF the Name does NOT contain "Critical" OR (i.e., if it does) if the descriptions match. And, if you think about it, that means the bit in the parentheses ("i.e, if...") become irrelevant. If the descriptions match then we always add the student whether or not it contains "Critical". And we also add the one without "Critical" That can be rendered as:

UPDATED: octavioccl pointed out an error.

var filteredStudent = students
                  .Where(s=>!s.subjects.Any(x => x.Sylabus.Name.Contains("Critical") || 
                            description.Contains(x.Description)))
                  .ToList();

NOTE: Given the update to the above, the second half would probably be faster than the first half, so we probably should reverse them.

var filteredStudent = students
                  .Where(s=>s.subjects
                    .Any(x => 
                        description.Contains(x.Description) ||
                        !x.Sylabus.Name.Contains("Critical"))) 
                  .ToList();

UPDATED (Again) Having thought about that overnight, I realized that doesn't work. So, let's try this again.... - We want all students EXCEPT those which have a subject containing "Critical", UNLESS that class's description matches. (important note -- despite there being a Linq method called Except() that's not what we want -- we actually want Where Not. SO, first stab at this is:

var filteredStudent = students
              .Where(s=>!s.subjects.Any(x => 
                          x.Sylabus.Name.Contains("Critical")
                          ?  !description.Contains(x.Description)
                          : false)
              .ToList();

So, here the inner-most lambda (x =>...) is a test if a particular subject is a reason to reject the student. If it does not contain "Critical", it's OK (via the false at the end). If it does, then it's rejected if the descriptions don't match.

However, from here we can shorten it. Consider what we just said, if the first test is false, then whole thing is false. If the first test is true, then the whole express is true or false based on th esecond test --- which is exactly the definition of "short-curcuited AND" hence:

var filteredStudent = students
              .Where(s=>!s.subjects.Any(x => 
                          x.Sylabus.Name.Contains("Critical")
                          &&  !description.Contains(x.Description))
              .ToList();

Read that we get, "We take all students except those who are taking any subject whose Sylabus Name contains "Critical" but does not have a matching description.

Upvotes: 2

DawidP
DawidP

Reputation: 16

I don't understand why you repeat the condition: student.subjects.Any(x => x.Syllabus.Name.Contains("Critical")), and then use the same result: filteredStudent.Add(student)?

If you want to add all items in list, do something like this:

    students.ToList().ForEach(s => filteredStudent.Add(s));

If you want to filter the result, add Where:

    students.Where(s => s.subjetcs.Any(x => x.Sylabus.Name.Contains("Critical") && description.Contains(x.Description)))
      .ToList().ForEach(s => filteredStudent.Add(s));

Maybe you want something like this:

    var list = new List<Stundent>();
    list.AddRange(studnets.Where(s => s.subjetcs.Any(x => x.Sylabus.Name.Contains("Critical") && description.Contains(x.Description)));
    list.AddRange(studnets.Where(s => s.subjetcs.All(x => !x.Sylabus.Name.Contains("Critical")));
    list.ForEach(x => filteredStudent.Add(x));

OR:

    students.Where(s => s.subjetcs.Any(x => x.Sylabus.Name.Contains("Critical") && description.Contains(x.Description)) || s.subjetcs.All(x => !x.Sylabus.Name.Contains("Critical"))
      .ToList().ForEach(s => filteredStudent.Add(s));

Upvotes: 0

Sergey Kalinichenko
Sergey Kalinichenko

Reputation: 727047

The condition for adding to the list of results is as follows:

Sylabus.Name does not contain "Critical", or Sylabus.Name contains "Critical" and the description contains Description.

This can be coded into a single Where condition.

Upvotes: 1

Related Questions