views:

4601

answers:

10

I am looking for a very fast way to filter down a collection in C#. I am currently using generic List<object> collections, but am open to using other structures if they perform better.

Currently, I am just creating a new List<object> and looping thru the original list. If the filtering criteria matches, I put a copy into the new list.

Is there a better way to do this? Is there a way to filter in place so there is no temporary list required?

A: 

That is going to be blazingly fast. Is it causing your system to slow? Is is a huge list? Otherwise, I wouldn't worry.

IainMH
+21  A: 

If you're using C# 3.0 you can use linq, way better and way more elegant:

List<int> myList;

List<int> filteredList = myList.where( x => x > 7)
Jorge Córdoba
The Where extension method returns IEnumerable<T>, not List<T>. It should be: myList.Where(x => x > 7).ToList()
Rafa Castaneda
Thanks Rafa for your comment.
David
+4  A: 

List has FindAll method that will do the filtering for you and return a subset of the list.

The msdn has a great code example here: http://msdn.microsoft.com/en-us/library/aa701359(VS.80).aspx

EDIT: I wrote this before I had a good understanding of Linq and the Where() method. If I were to write this today i would probably use the method Jorge mentions above. The FindAll method still works if you're stuck in a .NET 2.0 environment though.

Mykroft
+2  A: 

To do it in place, you can use the RemoveAll method of the "List<>" class along with a custom "Predicate" class...but all that does is clean up the code... under the hood it's doing the same thing you are...but yes, it does it in place, so you do same the temp list.

Adam Haile
+3  A: 

You can use IEnumerable to eliminate the need of a temp list.

public IEnumerable<T> GetFilteredItems(IEnumerable<T> collection)
{
    foreach (T item in collection)
    if (Matches<T>(item))
    {
        yield return item;
    }
}

where Matches is the name of your filter method. And you can use this like:

IEnumerable<MyType> filteredItems = GetFilteredItems(myList);
foreach (MyType item in filteredItems)
{
    // do sth with your filtered items
}

This will call GetFilteredItems function when needed and in some cases that you do not use all items in the filtered collection, it may provide some good performance gain.

Serhat Özgel
+2  A: 

You can use the FindAll method of the List, providing a delegate to filter on. Though, I agree with @IainMH that it's not worth worrying yourself too much unless it's a huge list.

bdukes
A: 

If you're using C# 3.0 you can use linq

Or, if you prefer, use the special query syntax provided by the C# 3 compiler:

var filteredList = from x in myList
                   where x > 7
                   select x;
Tom Lokhorst
+3  A: 

Here is a code block / example of some list filtering using three different methods that I put together to show Lambdas and LINQ based list filtering.

    #region List Filtering

    static void Main(string[] args)
    {
        ListFiltering();
        Console.ReadLine();
    }

    private static void ListFiltering()
    {
        var PersonList = new List<Person>();

        PersonList.Add(new Person() { Age = 23, Name = "Jon", Gender = "M" }); //Non-Constructor Object Property Initialization
        PersonList.Add(new Person() { Age = 24, Name = "Jack", Gender = "M" });
        PersonList.Add(new Person() { Age = 29, Name = "Billy", Gender = "M" });

        PersonList.Add(new Person() { Age = 33, Name = "Bob", Gender = "M" });
        PersonList.Add(new Person() { Age = 45, Name = "Frank", Gender = "M" });

        PersonList.Add(new Person() { Age = 24, Name = "Anna", Gender = "F" });
        PersonList.Add(new Person() { Age = 29, Name = "Sue", Gender = "F" });
        PersonList.Add(new Person() { Age = 35, Name = "Sally", Gender = "F" });
        PersonList.Add(new Person() { Age = 36, Name = "Jane", Gender = "F" });
        PersonList.Add(new Person() { Age = 42, Name = "Jill", Gender = "F" });

        //Logic: Show me all males that are less than 30 years old.

        Console.WriteLine("");
        //Iterative Method
        Console.WriteLine("List Filter Normal Way:");
        foreach (var p in PersonList)
            if (p.Gender == "M" && p.Age < 30)
                Console.WriteLine(p.Name + " is " + p.Age);

        Console.WriteLine("");
        //Lambda Filter Method
        Console.WriteLine("List Filter Lambda Way");
        foreach (var p in PersonList.Where(p => (p.Gender == "M" && p.Age < 30))) //.Where is an extension method
            Console.WriteLine(p.Name + " is " + p.Age);

        Console.WriteLine("");
        //LINQ Query Method
        Console.WriteLine("List Filter LINQ Way:");
        foreach (var v in from p in PersonList
                          where p.Gender == "M" && p.Age < 30
                          select new { p.Name, p.Age })
            Console.WriteLine(v.Name + " is " + v.Age);
    }

    private class Person
    {
        public Person() { }
        public int Age { get; set; }
        public string Name { get; set; }
        public string Gender { get; set; }
    }

    #endregion
Jon Erickson
A: 

Thanks Tom! ;)

A: 

Using Linq is relatively mush slower than using a predicate supplied to the Lists FindAll method. Also have to be careful with Linq as the eumeration of the list is not actually executed until you access the result. This can mean that when you think you have created a filtered list, the content may differ to what you expected when you actually read it.

gouldos