Monday, March 05, 2007 11:51 AM
.NET has a little known delegate known as the Predicate which can make searching much easier and much cleaner. Predicates are simply user defined boolean conditions which we can define right in code to help us sort through collections such as arrays.
Let's say, for example, that I had an array of numbers and I wanted to find all of the numbers which were odd. The obvious way is simply to loop through them checking each number for its "oddness" and adding it to an output array, like so:
int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int[] odds = GetOdds(numbers);
private static int[] GetOdds(int[] numbers)
{
int[] odds = new int[numbers.Length];
int counter = 0;
for (int i = 0; i < numbers.Length; i++)
{
if ((numbers[i] % 2) != 0)
{
// Found an odd!
odds[counter++] = numbers[i];
}
}
return odds;
}
Sure, this works...but it's a little ugly. Especially for such a trivial command, plus due to how we initialized our odds array we're going to have some empty blank strings at the end that we'll need to account for. In fact, when we really look at it the majority of our method seems to be just overhead to support the output array, looping through the input array, etc. The core logic itself is actually constrained to just one line:
if ((numbers[i] % 2) != 0)
It seems like that there should be a cleaner way to do this and, lucky for us, there is! Using Predicates, we can actually just write this little line of code into a stand alone method and reference it directly from the Array.FindXXX() methods. Check it out...
int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int[] odds = Array.FindAll<int>(numbers, IsOdd);
private static bool IsOdd(int number)
{
return ((number % 2) != 0);
}
Now the only thing that we have to worry about is our core logic, .NET takes care of all of the additional overhead for us!
We can even reference it inline using anonymous delegates! Check out how we pull just the objects of type Dog from this array using another predicate...
Animal[] animals = { new Dog("Fido"), new Cat("Fluffy"),
new Dog("Bruiser"), new Cat("Leroy")};
Dog[] allDogsGoToThisArray =
Array.FindAll<Animal>(animals,
Or we can even do it C# 3.0 style courtesy of lambda expressions...
Dog[] allDogsGoToThisArray =
Array.FindAll<Animal>(animals,
animal => (animal is Dog));
We can use this technique for any of the Find methods off of the Array class (Find, FindAll, FindIndex, FindLast, and FindLastIndex) as well as the Exists method to see if any objects matching our condition exist in the array. Check it out...
bool hasACat = Array.Exists<Animal>(animals, IsCat);
private static bool IsCat(Animal animal)
{
return (animal is Cat);
}
The counterpart of this method would be the TrueForAll variation which will tell us if all of our animals our Cats.
Array.TrueForAll<Animal>(animals, IsCat);
Hopefully this will help clean up some of those annoying routines you have scattered through your code that are just in place solely to get what you need from an array, check if an array has what you need, or just pull out a certain type of object from your array. Next time we'll talk about the Predicate's closely related cousin the Action and how he can help you quickly and cleanly perfom actions to an entire list of objects.
