PHP array_filter с аргументами

108

У меня такой код:

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

который я могу использовать для фильтрации такого массива:

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

Как я могу добавить аргументы в lower_than_10, чтобы он также принимал число для проверки? Мол, если есть такое:

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

как вызвать его из array_filter, передав от 10 до $ num или любого другого числа?

фистаккио
источник

Ответы:

64

В качестве альтернативы решению @ Charles, использующему замыкания , вы можете найти пример в комментариях на странице документации. Идея состоит в том, что вы создаете объект с желаемым состоянием ( $num) и методом обратного вызова (принимающим $iв качестве аргумента):

class LowerThanFilter {
        private $num;

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

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

Использование ( демонстрация ):

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

В качестве примечания, теперь вы можете заменить его LowerThanFilterна более общий NumericComparisonFilterс такими методами, как isLower, isGreaterи isEqualт.д. Просто мысль - и демонстрация ...

Jensgram
источник
Хорошее решение. В целях поддержки кода можно изменить класс для поддержки более удобочитаемых вызовов методов: $ matches = $ myobj-> ArraySelect (Array ('from' => $ arr, 'where' => $ foo, 'lessthan' => 12))
dreftymac
Я не разбираюсь в php, поэтому, возможно, это очевидный вопрос, но как вы можете передать массив в array_filter и при этом заставить его работать? в документации об этом никогда не говорится, кроме чьего-то комментария.
Никола Педретти
1
@NicolaPedretti Я полагаю, вы говорите о аргументе секунды array_filter? Это просто callable; в приведенном выше случае соответствие «Тип 3: вызов метода объекта»:, array(<instance>, <method-name>)ср. PHP: Обратные вызовы / Callables - Руководство .
Йенсграм
Интересный. Мне это действительно кажется хакерским. Передача метода напрямую кажется более интуитивным.
Никола Педретти
@nicolapedretti Я не касался PHP несколько лет. Сейчас мне кажется, что это
хакерское занятие
261

если вы используете php 5.3 и выше, вы можете использовать закрытие для упрощения кода:

$NUM = 5;
$items = array(1, 4, 5, 8, 0, 6);
$filteredItems = array_filter($items, function($elem) use($NUM){
    return $elem < $NUM;
});
ЧЖЕНЦЗИН ЛИАН
источник
12
Не знал, что вы можете использовать это useслово для предоставления лямбда дополнительных параметров. Спасибо за такой ценный совет! :)
Хулио Мария Мека Хансен
15
На мой взгляд, это лучшее решение. Все просто и по делу. Жаль, что PHP не позволяет анонимным функциям использовать переменные, объявленные в родительской области, как в javascript.
NadiaFaya
4
Полезный, элегантный, короткий, +1
Grokking
7
Я считаю, что это должно быть приемлемое решение, поскольку оно единственное, что отвечает на вопрос: «как добавить аргументы в array_filter» . Другие ответы предоставляют альтернативные маршруты к тому же результату с использованием либо закрытия, либо классов.
Тао
Спасибо приятель. Perfect
Arunjith RS
37

В PHP 5.3 или выше вы можете использовать закрытие :

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);
Чарльз
источник
1
спасибо за решение, оно отличное, но у меня на сервере установлен php 5.2, так что я обязательно буду использовать jensgram :)
histacchio
В php <5.3 вы можете использовать create_function().
Decent Dabbler
3
create_function()в основном eval()имеет другое имя и столь же злой. Его использование не рекомендуется. Дурацкий обходной путь на основе классов, приведенный в принятом ответе, является лучшим решением, чем использование create_function()в этом случае.
Charles
20

если вам нужно передать в функцию несколько параметров, вы можете добавить их к оператору use, используя ",":

$r = array_filter($anArray, function($anElement) use ($a, $b, $c){
    //function body where you may use $anElement, $a, $b and $c
});
Mar Bar
источник
14

В расширении ответа jensgram вы можете добавить еще немного магии, используя __invoke()магический метод.

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

Это позволит вам сделать

$arr = array(7, 8, 9, 10, 11, 12, 13);
$matches = array_filter($arr, new LowerThanFilter(12));
print_r($matches);
Стефан Гериг
источник
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;
    }
}


}

Это позволяет фильтровать элементы в многомерных массивах:

$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')) );
Маркос Басуальдо
источник