Peter's Gekko

Sponsors

The Lounge

Wicked Cool Jobs

News

Advertisement

Images in this post missing? We recently lost them in a site migration. We're working to restore these as you read this. Should you need an image in an emergency, please contact us at imagehelp@codebetter.com
Parameterized searching in a List<T>

The generic List is a pretty powerful class. But finding one or more items is not that easy to grasp at first sight. The Find and the FindAll method take a so called Predicate as a parameter to test the individual items in a List. The documentation on this on MSDN is not very obvious. In a rather long example you will not get any further than finding items on hard coded criteria. In the real world you usually want to find items on dynamic criteria. There is some literature on the web on this but the solutions vary. Hereby a roundup.

In a small example I will also work, just like MSDN, with a list of dinosaurus names

private static List<string> buildAnimals()

{

   List<string> animals = new List<string>();

   animals.Add("Diplodocus");

   animals.Add("Tyranosaurus");

   animals.Add("Protoceratops");

   animals.Add("Deinonichus");

   animals.Add("Stegosaurus");

   return animals;

}

In the example a selection of these names will populate a listbox. Pure presentation code.

The FindAll method takes a Predicate as a parameter. A predicate is a delegate, a definition of a method signature. The predicate method has one parameter, of the same type as the members of the list and returns a Boolean. Returning true signifies the list member does satisfy the selection criteria, returning false is a no match. When searching the list the Find(All) method will pass each list member to the method.

This is the predicate used all over the MSDN example

private static Boolean findAnimalMSDN(string name)

{

    return name.EndsWith("saurus");

}

Now to parameterize this search we have to get the search criteria into this method. It cannot be passed as a method parameter, an extra member is needed.

private static string findAnimal;

   

private static Boolean findAnimal1(string name)

{

    return name.EndsWith(findAnimal);

}

   

private void button1_Click(object sender, EventArgs e)

{

    List<string> animals = buildAnimals();

    listBox1.Items.Clear();

    findAnimal = textBox1.Text;

    List<string> matchinganimals = animals.FindAll(findAnimal1);

    foreach (string s in matchinganimals)

        listBox1.Items.Add(s);

}

   

Just like all MSDN example code the predicate is here a static member. To get the non static textbox value into the predicate requires a static helper string.

But the predicate does not have to be static. An instance method can read the contents of the textbox itself.

private Boolean findAnimal2(string name)

{

    return name.EndsWith(textBox1.Text);

}

   

private void button2_Click(object sender, EventArgs e)

{

    List<string> animals = buildAnimals();

    listBox1.Items.Clear();

    List<string> matchinganimals = animals.FindAll(findAnimal2);

    foreach (string s in matchinganimals)

        listBox1.Items.Add(s);

}

But still this code is pretty smelly, I don't like it at all that there are two methods needed to get just one thing done. There is an article on CodeProject by Alex Perepletov where he discusses some of these issues and creates a class to wrap everything up. But I still think that is an overkill. After all I don't want to have to write a complete class to get something very simple done.

Using anonymous delegates in C# you can combine both methods in one. Instead of writing the predicate and using it as a parameter to the FindAll method you just pass the complete method itself as a parameter.

private void button4_Click(object sender, EventArgs e)

{

    List<string> animals = buildAnimals();

    listBox1.Items.Clear();

    List<string> matchinganimals = animals.FindAll(

        delegate(string name) { return name.EndsWith(textBox1.Text); }

        );

    foreach (string s in matchinganimals)

        listBox1.Items.Add(s);

}

The parameter is the complete method, the method signature is a delegate.

The method does not need a name, it is anonymous. In a short but clear reference on thinksharp it is named an inline delegate. The official name is anonymous delegate. The code is inline, note how it can read (and set) it's surrounding scope. Here it can read the contents of the textbox. An anonymous delegate can also read from the scope it is defined in. A variation:

private void button3_Click(object sender, EventArgs e)

{

    List<string> animals = buildAnimals();

    listBox1.Items.Clear();

    string privateAnimal = textBox1.Text;

    List<string> matchinganimals = animals.FindAll(

        delegate(string name) { return name.EndsWith(privateAnimal); }

        );

    foreach (string s in matchinganimals)

        listBox1.Items.Add(s);

}

Here it works with the local privateAnimal string.

In the example I have worked with a list of strings. But working with complete methods to test list members there is no limit on the complexity of the comparison.

Well, that's about it. Then there is the obvious question: Is this code available in VB ? It's pretty funny what the translator comes up with. But no there are no anonymous delegates in VB.


Posted Tue, Oct 2 2007 10:23 PM by pvanooijen
Filed under:

[Advertisement]

Comments

kolen wrote re: Parameterized searching in a List<T>
on Fri, Feb 22 2008 2:56 PM

I used typed collections previously where i implement methods to retrieve the member form the colection by the specified identifier. Now i am trying to apply this in the generic versions in this way

List<MyClass> myGenList = new List<MyClass>();

... filled with some objects

now need To determine whether myGenList Contains some instance by the instance's identifier

MyClass searchedObject = new MyClass();

... searchedObjecet is retrieved from somewhere else - those objects expose some property "ID" where unique identified is stored

MyClass foundObject = myGenList.Find(delegate(MyClass objFromMyGenList) { return objFromMyGenList.ID==searchedObjecet.ID; };

           if(foundObject != null)

               searchedObjecet is in the list

           else

               searchedObjecet is NOT in the list

Am i using this in the right way?

Is there any better way to implement retrieval for some instance stored in a generic list (collection) based on a value of some of the instance's properties?

pvanooijen wrote re: Parameterized searching in a List<T>
on Fri, Mar 21 2008 4:46 PM

You can do it shorter in  C# 3.0 using a lambda expression

That would make

MyClass foundObject = myGenList.Find((MyClass objFromMyGenList) => objFromMyGenList.ID==searchedObjecet.ID));

It's not better, just a shorter notation

Morris wrote re: Parameterized searching in a List<T>
on Sun, Jun 8 2008 3:04 PM

Thanks! This was just what I needed. I couldn't believe the Find method required a hard coded value in the delegate, but that was what all the examples had. Would it have been so hard for MSDN to show an example like your's?? I'm kicked the habit of using variables that don't exist in the local or class scope a long time ago, so it would have been awhile before I tried what you did just because C taught me many painful lessons in being careful with scope.

Add a Comment

(required)  
(optional)
(required)  
Remember Me?
Devlicio.us