CodeBetter.Com
CodeBetter.Com
RSS 2.0 via Feedburner
           Do you Twitter? Follow us @CodeBetter

Jeffrey Palermo [MVP]

Software management consultant and CTO, Headspring Systems

Searching in .Net (don't search manually, let .Net do it for you) - level 100

With .Net 2.0, we have some basic search functionality baked in to the .Net Framework.  With Linq coming out in .Net 3.5, we will have full querying support on IEnumerable types, but we have quite a bit of power with .Net 2.0.  In this short article, I'll outline how to search objects in memory with .Net 2.0 or higher.

Consider the following class, Person:

public class Person : IComparable

{

    private string _firstName;

    private string _lastName;

 

    public Person(string firstName, string lastName)

    {

        _firstName = firstName;

        _lastName = lastName;

    }

 

    public string FirstName

    {

        get { return _firstName; }

    }

 

    public string LastName

    {

        get { return _lastName; }

    }

 

    public string FullName

    {

        get { return string.Format("{0} {1}", FirstName, LastName); }

    }

 

    public int CompareTo(object obj)

    {

        Person person = (Person)obj;

        int compareResult = LastName.CompareTo(person.LastName);

        if(compareResult == 0)

        {

                compareResult = FirstName.CompareTo(person.FirstName);

        }

 

        return compareResult;

    }

 

    public override string ToString()

    {

        return FullName;

    }

}


Consider this a basic domain object within the application.  We have the need to work with many instances of this class in memory.  Assume that there is no need for a relational database for this application.  We pull Person instances in memory and work with them.  Often, we'll need to pare down the set to only include instances that match some criteria.  We could loop through each one and create a new list with matches, but with .Net 2.0 and "Comparison"(s), we have a more elegant way.  Here is an example.

        public void ShouldQueryArray()

        {

            Person[] people = new Person[]{new Person("Homer", "Simpson"), new Person("Jeffrey", "Palermo")};

            Person[] matches = Array.FindAll<Person>(people, delegate(Person obj)

                                                                {

                                                                    return (obj.FirstName.Contains("ome"));

                                                                });

 

            foreach(Person person in matches)

            {

                Console.WriteLine(person);

            }

        }


In this example, we can just specify the condition that causes an instance to match, and we'll get back a new array with only the matches.  In this case, we get the following output:

Homer Simpson

I work with arrays a lot.  In fact, it is my favorite IEnumerable data structure because of it's power, but it isn't the only structure that supports comparisons (and there are other methods similar to FindAll, but FindAll is the most common).  

public void ShouldQueryIList()

{

    List<Person> list = new List<Person>();

    list.Add(new Person("Homer", "Simpson"));

    list.Add(new Person("Jeffrey", "Palermo"));

 

    List<Person> matches = list.FindAll(delegate(Person obj)

                                            {

                                                return (obj.FirstName.Contains("ome"));

                                            });

 

    foreach (Person person in matches)

    {

        Console.WriteLine(person);

    }

}


Here, we are using the List<T> class for our search.  The following example is more complex.  This illustrates that the matching criteria can be as complicated as you need it to be.

public void ShouldQueryArrayWithComplexComparison()

{

    Person[] people = new Person[] { new Person("Homer", "Simpson"), new Person("Jeffrey", "Palermo") };

    Person[] matches = Array.FindAll<Person>(people, delegate(Person obj)

                                                        {

                                                            return (obj.FirstName.StartsWith("j", StringComparison.InvariantCultureIgnoreCase)

                                                                   && obj.LastName.EndsWith("mo"));

                                                        });

 

    foreach (Person person in matches)

    {

        Console.WriteLine(person);

    }

}


By the way, if you aren't familiar with the "delegate" keyword, this is an anonymous method.  I could refactor it into a private method, but I'm using it inline instead.  In short, FindAll just requires a delegate.  However you wish to provide it is fine.






Comments

Frank Zehelein said:

Whar is really nice with that approach (using delegate keyword and not a seperate method) you can access local variables from method ShouldQueryArrayWithComplexComparison - this way you can bring in varibles to compare.

So instead of having "j" in the delegate method you could define it or get it as a parameter into your ShouldQueryArrayWithComplexComparison method

I really like that feature of delegates, .ForAll, .ForEach, ...

# December 4, 2006 8:57 AM

Jeremy D. Miller said:

Ruby has much cleaner syntax and much more capabilities for this Jeffrey;)

# December 6, 2006 11:33 AM

cfields said:

Jeremy - Just because you and I have not had a healthy debate in a while --- ;)

It is the ruby gem activerecord that has the awesome find syntax.

# December 11, 2006 8:23 AM

Michal Talaga said:

I would remove the IComparable interface and its implementation from this scenario since it isn't used anywhere and only adds unncessary code.

Other than that, I agree that it is a nice feature to have.

# February 2, 2007 5:43 AM

About Jeffrey Palermo

Jeffrey Palermo is a software management consultant and the CTO of Headspring Systems in Austin, TX. Jeffrey specializes in Agile coaching and helps companies double the productivity of software teams. Jeffrey is an MCSD.Net , Microsoft MVP, Certified Scrummaster, Austin .Net User Group leader, AgileAustin board member, INETA speaker, INETA Membership Mentor, Christian, husband, father, motorcyclist, Eagle Scout, U.S. Army Veteran, and Texas A&M University graduate. Check out Devlicio.us!

This Blog

Syndication