Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Simplest way to filter value from generic List in C# using LINQ

Tags:

c#

linq

I have two classes. The first one is Person, and the second one is Student (which inherits from Person). I want to filter a generic List and find all Students which grades are higher than 7. I came up with the following solution:

class Person
{
    public string Name {get; set;}
}

class Student : Person
{
    public decimal Grade {get; set;}
}

class Program
{
    static void Main(string[] args)
    {
        List<Person> people = new List<Person>();
        people.Add(new Person() {Name="John"});
        people.Add(new Student() {Name="Joe", Grade=6});
        people.Add(new Student() {Name="Jane", Grade=8});

        people.Where(delegate (Person person) {
            var student = person as Student;
            return student != null && student.Grade > 7;
        });
    }
}

Is there a simpler way to filter this list?

like image 271
Eduardo Matos Avatar asked Jun 22 '13 22:06

Eduardo Matos


3 Answers

The only improvement I see is using OfType, like this

var result = people.OfType<Student>().Where(s => s.Grade > 7);

...and my syntax is simpler... but that is in the eye of the beholder.

like image 149
Hogan Avatar answered Oct 25 '22 16:10

Hogan


Here's a few different ways of doing it, with some relative performance numbers:

Initial

people.Where(delegate(Person person)
{
    var student = person as Student;
    return student != null && student.Grade > 7m;
});

Initial Modified (same speed as Initial)

people.Where(p =>
{
    var student = p as Student;
    return student != null && student.Grade > 7m;
});

OfType (40-52% SLOWER than Initial)

people.OfType<Student>().Where(s => s.Grade > 7m)

Foreach (9-16% faster than Initial)

var results = new List<Student>();
foreach (var person in people)
{
    var student = person as Student;
    if (student != null && student.Grade > 7m)
    {
         results.Add(student);
    }
}

For (12-18% faster than initial)

var results = new List<Student>();
for (var idxPerson = 0; idxPerson < people.Count; idxPerson++)
{
    var student = people[idxPerson] as Student;
    if (student != null && student.Grade > 7m)
    {
        results.Add(student);
    }
}

To get the performance numbers, I:

  • Ran a control test that did nothing
  • Timed each of the functions 100 times over a List with 10 to 1,000,000 elements
  • Subtracted the control time from each tests time (to make the results more accurate)
  • Each function used the same random data set and ToList was used to force the enumerator to run

Of course, these are just performance numbers on my machine, you'll have to test on real-world data to get actual results as the distribution of Students vs. People, the average grade of the student, etc. will cause a lot of variation in the timings.

like image 4
steve Avatar answered Oct 25 '22 15:10

steve


people.RemoveAll(p => p.Grade <= 7);
like image 1
Jannie Theunissen Avatar answered Oct 25 '22 17:10

Jannie Theunissen