PHP Array_Filter

filter Yesterday I was waxing my car, you know, "Wax on, wax off." and an epiphany hit me. What if I wrote an article on array_filter?

I can't tell you how many times I've had to run an array of values around a foreach loop just to get a subset of the array, of course, that was in my earlier years, before I knew about array_filter.

Well, today's your lucky day. We're going to crush the PHP array_filter function, yep, like a squished grape. So much so, that when you want a subset of an array, you'll come to good old "geekgumbo.com" and dial the "PHP Array Filter" article right up. Now before I go any further, I'm putting in a plug for my free variable checker, newchk, which made all the pretty results pictures you see in this article.

Array_filter does just what it says it does, it filters an existing array by a criteria set up in a callback function. We'll create quite a few callback functions in this article so you can cut and paste them for future use, making your use of array_filter that much easier.

The syntax for array filter looks like this:

$result = array_filter( [the data array you want to filter], ['the name of the callback function you want to use to filter the array']);

The name of the callback function should be quoted as a string. The output of the array_filter function is another array of the filtered results.

Let's create the mother of all mixed arrays to use as our array to filter throughout this article.

$dataArr = array();
$dataArr = array(-1, 0, 1, -21, 34, -4.12, 5.67, 2.34, -1.71,
    'Null', Null, '', 'banana', 'Bummer', 'Carl', 'crammer');

That's a whale of a mixed array: integers, zero, floats, positive, negative, odd, even, lower case, upper case, strings, and empty nulls. Using my newchk variable checker, "ding ding", lets see what the array looks like without filtering.

af_all600What a mess! Let's do some filtering.

TAKING OUT THE NULLS, AND EMPTIES

If you call array_filter without a callback it thins out the array of all your nulls, nice to use after a database retrieve.

$result = array_filter($dataArr);

That's it, and it gives you this:

af_nonulls600

You'll notice that the two nulls, keys 10 and 11, are taken out of the new $result array. But don't be fooled, if the there are quotes around Null, like so 'Null', that's a valid string, and it will stay in the array. Also, notice that the original keys are saved. The keys jump from 9 to 12 with 10 and 11 missing. If you want to re-index the array, use array_values, like so:

$result = array_filter($dataArr);
$result = array_values($result);

And we get:

af_keysOrdered600Now the keys are numbered sequentially.

FILTER FUNCTIONS

Let's create some callback functions, for you to use with array_filter, now, and forever more.

// Functions to use with array_filter

function positive($dataArr)
{
    return ($dataArr > 0) ? true : false;
}

function negative($dataArr)
{
    return ($dataArr < 0) ? true : false;
}

// put this here ,if you don't read too well
function noNulls($dataArr)
{
    return $dataArr;
}

function removeStrings($dataArr)
{
    if (is_numeric($dataArr))
        return $dataArr;
}

function removeNums($dataArr)
{
    if (!is_numeric($dataArr))
        return $dataArr;
}

function intOnly($dataArr)
{
    if (is_int($dataArr))
        return $dataArr;
}

function floatOnly($dataArr)
{
    if (is_float($dataArr))
        return $dataArr;
}

// the odd and even functions work on either int or float, or both together
function odd($dataArr)
{
    if (is_numeric($dataArr)) {
        if (is_float($dataArr)) {
            $rem = (substr($dataArr, -1)) % 2;
            if ($rem == 1) return $dataArr;
        }
        if (is_int($dataArr) && (1 & $dataArr) != 0) return $dataArr;       
    }
}

function even($dataArr)
{
    if (is_numeric($dataArr)) {
        if (is_float($dataArr)) {
            $rem = (substr($dataArr, -1)) % 2;
            if ($rem == 0) return $dataArr;
        }
        if (is_int($dataArr) && (1 & $dataArr) == 0) return $dataArr;
    }
}

TESTING THE CALLBACK FUNCTIONS

OK, We have a slew of functions, let's tests them. I'll give you the call to array_filter and then the output of that call. I'll use the original data array we started with, and I'll run them quickly one after the other, so you can see the results. You can click on the picture and zoom in, or better zoom into the browser window, if you want a clearer image.

// $result = array_filter($dataArr, 'positive'); positive numbers only

af_positive600

// $result = array_filter($dataArr, 'negative'); negative numbers only

af_negative600

// $result = array_filter($dataArr, 'removeStrings'); remove all the strings

af_removeStrings600

// $result = array_filter($dataArr, 'removeNums'); remove all the numbers

af_removenums600

// $result = array_filter($dataArr, 'intOnly'); integers only

af_intonly600

// $result = array_filter($dataArr, 'floatOnly'); floats only

ar_floatOnly600

// $result = array_filter($dataArr, 'odd'); odd numbers ints and floats

af_odd600

// $result = array_filter($dataArr, 'even'); even numbers ints and floats

af_even600

There is nothing stopping you from doing something like this, and filtering the array twice to get what you want.

$result = array_filter($dataArr, 'intOnly');
$result2 = array_filter($result, 'odd');

 

STRINGS

We haven't covered filtering strings. I can't tell you how many times, I've just wanted arrays that started with a letter, or two or more letters, in the case of the states of the U.S., for example. We'll take a slightly different approach with strings, and use an anonymous function, or closure, which works in PHP 5.3 and above. We're using a closure, because array_filter() only accepts the name of the function, and does not allow the passing of parameters with the function.  Anonymous functions do.

We are essentially writing a function within the array_filter call. The parameters we need ahead of time are the first letters of the strings you want in your new filtered array, and the length of the letters in each of elements of your array of letters. You can't search for the letter 'b' and 'bum' in the same array. The letters all have to be the same length in the array. However, there's nothing stopping you from filtering twice as shown above.

// the letters you want to search for, notice they are case sensitive. So a 'b' is not a 'B'.
$ltrs = array('ba', 'Ca');

// the letter length in the array
$ltrsUsed = 2;

// here's the closure
    $result = array_filter($dataArr, function($dataArr) use ($ltrs, $ltrsUsed ) {
        if (in_array(substr($dataArr, 0, $ltrsUsed), $ltrs)) {
            return $dataArr;
        }
    });

We go through our data array element-by-element getting the first two letters in each element of the array, we then see if the two letter element, we just got, is in the $ltrs array, if it is, we put the results in the new array.  The "function" and "use" sets up the anonymous function in the array_filter call, which allow us to enter our parameters, and the array_filter call runs through the array one at a time.  And here's what it look like:

af_string600

Well, I think we squished the grape so to speak.  You can see that array_filter can save you some time and effort. The strings and the odd and even functions took some time to create and get right. I haven't seen either of these solutions listed anywhere else on the web. If you have other neat callbacks feel free to share them in your comments. Enjoy, and happy coding.