PHP array_filter con argumentos

108

Tengo el siguiente código:

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

que puedo usar para filtrar una matriz como esta:

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

¿Cómo puedo agregar argumentos a lower_than_10 para que también acepte el número para verificar? Como, si tengo esto:

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

¿Cómo llamarlo desde array_filter pasando 10 a $ num o cualquier número?

pistacho
fuente

Respuestas:

64

Como alternativa a la solución de @ Charles que usa cierres , puede encontrar un ejemplo en los comentarios en la página de documentación. La idea es que crees un objeto con el estado deseado ( $num) y el método de devolución de llamada (tomando $icomo argumento):

class LowerThanFilter {
        private $num;

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

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

Uso ( demostración ):

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

Como anotación al margen, ahora se puede sustituir LowerThanFiltercon una más genérica NumericComparisonFiltercon métodos como isLower, isGreater, isEqualetc. Es sólo una idea - y una demostración ...

jensgram
fuente
Buena solución. En aras del código mantenible, podría ser útil modificar la clase para que admita llamadas a métodos más legibles también: $ matches = $ myobj-> ArraySelect (Array ('from' => $ arr, 'where' => $ foo, 'lessthan' => 12))
dreftymac
No soy un experto en php, por lo que tal vez esta sea una pregunta obvia, pero ¿cómo puede pasar una matriz a array_filter y aún así hacer que funcione? la documentación nunca habla de esto, excepto por el comentario de alguien.
Nicola Pedretti
1
@NicolaPedretti Supongo que estás hablando del argumento de los segundos array_filter. Es simplemente un callable; en el caso anterior que coincide con "Tipo 3: llamada al método de objeto":, array(<instance>, <method-name>)cf. PHP: Callbacks / Callables - Manual .
jensgram
Interesante. Me parece realmente extraño. Pasar el método directamente parece más intuitivo.
Nicola Pedretti
@nicolapedretti No he tocado PHP durante varios años. A estas alturas, la mayor parte me parece hacky :)
jensgram
260

si usa php 5.3 y superior, puede usar el cierre para simplificar su código:

$NUM = 5;
$items = array(1, 4, 5, 8, 0, 6);
$filteredItems = array_filter($items, function($elem) use($NUM){
    return $elem < $NUM;
});
ZHENJiNG LiANG
fuente
12
No sabía que podía usar la usepalabra para proporcionar parámetros adicionales a la lambda. ¡Gracias por una pista tan valiosa! :)
Julio María Meca Hansen
15
En mi opinión, esta es la mejor solución. Es simple y va al grano. Es una pena que PHP no permita que funciones anónimas usen variables declaradas en el ámbito principal, como en javascript.
NadiaFaya
4
Útil, elegante, corto, +1
Grokking
7
Creo que esta debería ser la solución aceptada, ya que es la única que responde a la pregunta: "cómo se pueden agregar argumentos a array_filter" . Las otras respuestas proporcionan rutas alternativas para el mismo resultado, utilizando el cierre o las clases.
tao
Gracias amigo. Perfecto
Arunjith RS
36

En PHP 5.3 o superior, puede usar un cierre :

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
fuente
1
gracias por la solución, está ordenada, pero tengo php 5.2 en el servidor, así que estoy obligado a usar jensgram's :)
pistacchio
En php <5.3 puedes usar create_function().
Decent Dabbler
3
create_function()es básicamente eval()con otro nombre, y es igual de malvado. Se debe desalentar su uso. La extraña solución basada en clases que se proporciona en la respuesta aceptada es una mejor solución que usar create_function()en este caso.
Charles
20

si necesita que se pasen varios parámetros a la función, puede agregarlos a la declaración de uso usando ",":

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

En extensión a la respuesta de jensgram , puede agregar un poco más de magia usando el __invoke()método mágico.

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);
    }
}

Esto te permitirá hacer

$arr = array(7, 8, 9, 10, 11, 12, 13);
$matches = array_filter($arr, new LowerThanFilter(12));
print_r($matches);
Stefan Gehrig
fuente
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;
    }
}


}

Esto le permite filtrar elementos en matrices multidimensionales:

$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')) );
Marcos Basualdo
fuente