118

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?

pistacchio
  • 50,394
  • 95
  • 256
  • 391

6 Answers6

284

if you a 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;
});
kamal pal
  • 4,049
  • 5
  • 22
  • 39
ZHENJiNG LiANG
  • 2,949
  • 2
  • 11
  • 2
  • 12
    Didn't know you could use the `use` word to provide the lambda with extra parameters. Thanks for such a valuable hint! :) – Julio María Meca Hansen Sep 24 '13 at 10:31
  • 18
    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. – NadiaFaya Sep 24 '13 at 15:08
  • 4
    Useful, elegant, short, +1 – Grokking Jan 14 '15 at 09:13
  • 8
    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. – tao Nov 25 '15 at 15:23
  • Thanks dude. Perfect – Arunjith R S Apr 03 '20 at 20:08
70

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...

Community
  • 1
  • 1
jensgram
  • 29,088
  • 5
  • 77
  • 95
  • 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 ) ) – dreftymac Nov 10 '11 at 00:31
  • 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. – Nicola Pedretti Aug 17 '17 at 17:40
  • 1
    @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(, )`, cf. [PHP: Callbacks / Callables - Manual](http://php.net/manual/en/language.types.callable.php). – jensgram Aug 18 '17 at 04:51
  • Interesting. It does feel really hacky to me. Passing the method directly seems more intuitive. – Nicola Pedretti Aug 18 '17 at 13:21
  • @nicolapedretti I haven't touched PHP for several years. By now most of it feels hacky to me :) – jensgram Aug 18 '17 at 17:45
  • Ok, so what when you want one argument more? In other words, this solution works for exactly 2 arguments? – nights Jun 01 '18 at 05:27
  • @nights If I get you right: `array_filter($arr, array(new BetweenFilter(9, 12), 'isBetween'))` with `class BetweenFilter { private $lower; private $upper; … function isBetween($input) { return $input >= $this->lower && $input <= $this->upper; }}`. Bounds are inclusive in this (untested!) example. – jensgram Jun 01 '18 at 06:46
  • what if the `isLower` requires 2 or three params? how would you specify those extra 2 params from within the `array_filter` – Reborn Feb 03 '20 at 01:30
  • @MuhammadOmerAslam Sorry to say, but I haven't touched PHP since writing this answer. Your question may be better suited as a separate thread. – jensgram Feb 03 '20 at 08:44
37

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);
Charles
  • 48,924
  • 13
  • 96
  • 136
  • 1
    thanks for the solution, it is neat, but i have php 5.2 on the server, so i'm bound to use jensgram's :) – pistacchio Mar 30 '11 at 08:27
  • In php < 5.3 you could use `create_function()`. – Decent Dabbler Mar 30 '11 at 17:40
  • 3
    `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. – Charles Mar 30 '11 at 17:43
21

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
});
Mar Bar
  • 407
  • 5
  • 11
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);
Community
  • 1
  • 1
Stefan Gehrig
  • 78,962
  • 24
  • 149
  • 181
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' => 'user1@email.com','name' => 'Robert');
$users[] = array('email' => 'user2@email.com','name' => 'Carl');
$users[] = array('email' => 'user3@email.com','name' => 'Robert');

//Print all users called 'Robert'
print_r( array_filter($users, new ArraySearcher('name',ArraySearcher::OPERATOR_EQUALS,'Robert')) );
animuson
  • 50,765
  • 27
  • 132
  • 142
Marcos Basualdo
  • 2,040
  • 3
  • 16
  • 11