147

I have the following code:

function lower_than_10($i) {
    return ($i < 10);
}

that I can use to filter an array like this:

$arr = array(7, 8, 9, 10, 11, 12, 13);
$new_arr = array_filter($arr, 'lower_than_10');

How can I add arguments to lower_than_10 so that it also accepts the number to check against? Like, if I have this:

function lower_than($i, $num) {
    return ($i < $num);
}

how to call it from array_filter passing 10 to $num or whatever number?

1

7 Answers 7

363

if you are using php 5.3 and above, you can use closure to simplify your code:

$NUM = 5;
$items = array(1, 4, 5, 8, 0, 6);
$filteredItems = array_filter($items, function($elem) use($NUM){
    return $elem < $NUM;
});
Sign up to request clarification or add additional context in comments.

4 Comments

Didn't know you could use the use word to provide the lambda with extra parameters. Thanks for such a valuable hint! :)
This is in my opinion the best solution. It's simple and to the point. It's a shame that PHP doesn't allow anonymous functions to use variables declared in the parent scope, like in javascript.
I believe this should be the accepted solution, as it is the only one that answers the question: "how can add arguments to array_filter". The other answers are providing alternative routes to the same result, using either closure or classes.
Take note that you can use multiple variables in the 3rd parameter, just separate each variable by a comma such that use($NUM, $var2)
76

As an alternative to @Charles's solution using closures, you can actually find an example in the comments on the documentation page. The idea is that you create an object with the desired state ($num) and the callback method (taking $i as an argument):

class LowerThanFilter {
        private $num;

        function __construct($num) {
                $this->num = $num;
        }

        function isLower($i) {
                return $i < $this->num;
        }
}

Usage (demo):

$arr = array(7, 8, 9, 10, 11, 12, 13);
$matches = array_filter($arr, array(new LowerThanFilter(12), 'isLower'));
print_r($matches);

As a sidenote, you can now replace LowerThanFilter with a more generic NumericComparisonFilter with methods like isLower, isGreater, isEqual etc. Just a thought — and a demo...

9 Comments

Good workaround. For the sake of maintainable code, it might help to modify the class to support more readable method calls as well: $matches = $myobj->ArraySelect( Array('from'=>$arr, 'where'=>$foo, 'lessthan'=>12 ) )
I am not a php savy, so maybe this is an obvious question, but how can you pass in an array to array_filter and still make it work? the documentation never talks about this, except for someone's comment.
@NicolaPedretti I assume you're talking about the seconds argument to array_filter? It's simply a callable; in the above case matching "Type 3: Object method call": array(<instance>, <method-name>), cf. PHP: Callbacks / Callables - Manual.
Interesting. It does feel really hacky to me. Passing the method directly seems more intuitive.
@nicolapedretti I haven't touched PHP for several years. By now most of it feels hacky to me :)
|
39

In PHP 5.3 or better, you can use a closure:

function create_lower_than($number = 10) {
// The "use" here binds $number to the function at declare time.
// This means that whenever $number appears inside the anonymous
// function, it will have the value it had when the anonymous
// function was declared.
    return function($test) use($number) { return $test < $number; };
}

// We created this with a ten by default.  Let's test.
$lt_10 = create_lower_than();
var_dump($lt_10(9)); // True
var_dump($lt_10(10)); // False
var_dump($lt_10(11)); // False

// Let's try a specific value.
$lt_15 = create_lower_than(15);
var_dump($lt_15(13)); // True
var_dump($lt_15(14)); // True
var_dump($lt_15(15)); // False
var_dump($lt_15(16)); // False

// The creation of the less-than-15 hasn't disrupted our less-than-10:
var_dump($lt_10(9)); // Still true
var_dump($lt_10(10)); // Still false
var_dump($lt_10(11)); // Still false

// We can simply pass the anonymous function anywhere that a
// 'callback' PHP type is expected, such as in array_filter:
$arr = array(7, 8, 9, 10, 11, 12, 13);
$new_arr = array_filter($arr, $lt_10);
print_r($new_arr);

3 Comments

thanks for the solution, it is neat, but i have php 5.2 on the server, so i'm bound to use jensgram's :)
In php < 5.3 you could use create_function().
create_function() is basically eval() with another name, and is just as evil. Using it should be discouraged. The wacky class-based workaround given in the accepted answer is a better solution than using create_function() in this case.
31

if you need multiple parameters to be passed to the function, you may append them to the use statement using ",":

$r = array_filter($anArray, function($anElement) use ($a, $b, $c){
    //function body where you may use $anElement, $a, $b and $c
});

Comments

14

In extension to jensgram answer you can add some more magic by using the __invoke() magic method.

class LowerThanFilter {
    private $num;

    public function __construct($num) {
        $this->num = $num;
    }

    public function isLower($i) {
        return $i < $this->num;
    }

    function __invoke($i) {
        return $this->isLower($i);
    }
}

This will allow you to do

$arr = array(7, 8, 9, 10, 11, 12, 13);
$matches = array_filter($arr, new LowerThanFilter(12));
print_r($matches);

Comments

9

Worth noting that since PHP 7.4 arrow functions are available, and this can be done even more neatly:

$max = 10;
$arr = array(7, 8, 9, 10, 11, 12, 13);
$new_arr = array_filter($arr, fn ($n) => $n < $max);

Comments

5
class ArraySearcher{

const OPERATOR_EQUALS = '==';
const OPERATOR_GREATERTHAN = '>';
const OPERATOR_LOWERTHAN = '<'; 
const OPERATOR_NOT = '!=';      

private $_field;
private $_operation;
private $_val;

public function __construct($field,$operation,$num) {
    $this->_field = $field;
    $this->_operation = $operation;
    $this->_val = $num;
}


function __invoke($i) {
    switch($this->_operation){
        case '==':
            return $i[$this->_field] == $this->_val;
        break;

        case '>':
            return $i[$this->_field] > $this->_val;
        break;

        case '<':
            return $i[$this->_field] < $this->_val;
        break;

        case '!=':
            return $i[$this->_field] != $this->_val;
        break;
    }
}


}

This allows you to filter items in multidimensional arrays:

$users = array();
$users[] = array('email' => '[email protected]','name' => 'Robert');
$users[] = array('email' => '[email protected]','name' => 'Carl');
$users[] = array('email' => '[email protected]','name' => 'Robert');

//Print all users called 'Robert'
print_r( array_filter($users, new ArraySearcher('name',ArraySearcher::OPERATOR_EQUALS,'Robert')) );

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.