Quite often I might have an array of objects, be it from a db query or some json object, and I want to filter that list in a particular way. Lots of times I would find myself doing the same old thing; creating a new array, looping and looping until all I had left was what matched my filter.
I’m sure you’ve been there and done it a thousand times, too.
Well, this little function should help that task out a lot!
Say I had an array of people objects and wanted only those people who’s name was Bob and was aged 35, I could do something as simple as:
[php]$filtered = ofilter($items, [‘name’ => ‘Bob’, ‘age’ => 35]);[/php]
Or maybe something a little tricker; I wanted to get anyone whose age was between 18 and 35 (inclusive):
[php]$filtered = ofilter($items, [‘age’ => function($age) { return ($age >= 18 && $age < = 35); }]);[/php]
Pretty easy, eh?
Here’s the code – it’s a GitHub gist, so feel free to fork and improve!
<?php
/**
* Filter an array of objects.
*
* You can pass in one or more properties on which to filter.
*
* If the key of an array is an array, then it will filtered down to that
* level of node.
*
* Example usages:
* <code>
* ofilter($items, 'size'); // filter anything that has value in the 'size' property
* ofilter($items, ['size' => 3, 'name']); // filter anything that has the size property === 3 and a 'name' property with value
* ofilter($items, ['size', ['user', 'forename' => 'Bob'], ['user', 'age' => 30]) // filter w/ size, have the forename value of 'Bob' on the user object of and age of 30
* ofilter($items, ['size' => function($prop) { return ($prop > 18 && $prop < 50); }]);
* </code>
*
* @param array $array
* @param string|array $properties
* @return array
*/
function ofilter($array, $properties)
{
if (empty($array)) {
return;
}
if (is_string($properties)) {
$properties = [$properties];
}
$isValid = function($obj, $propKey, $propVal) {
if (is_int($propKey)) {
if (!property_exists($obj, $propVal) || empty($obj->{$propVal})) {
return false;
}
} else {
if (!property_exists($obj, $propKey)) {
return false;
}
if (is_callable($propVal)) {
return call_user_func($propVal, $obj->{$propKey});
}
if (strtolower($obj->{$propKey}) != strtolower($propVal)) {
return false;
}
}
return true;
};
return array_filter($array, function($v) use ($properties, $isValid) {
foreach ($properties as $propKey => $propVal) {
if (is_array($propVal)) {
$prop = array_shift($propVal);
if (!property_exists($v, $prop)) {
return false;
}
reset($propVal);
$key = key($propVal);
if (!$isValid($v->{$prop}, $key, $propVal[$key])) {
return false;
}
} else {
if (!$isValid($v, $propKey, $propVal)) {
return false;
}
}
}
return true;
});
}
<?php
$a = [
(object)['size' => 15, 'person' => (object)['name' => 'Andy', 'job' => 'developer'], 'pet' => 'dog'],
(object)['size' => 9, 'person' => (object)['name' => 'Candy', 'job' => 'stripper'], 'pet' => 'hamster'],
(object)['person' => (object)['name' => 'Bubbles', 'job' => 'painter'], 'pet' => 'fish'],
(object)['person' => (object)['name' => 'Bob', 'job' => 'Breeder'], 'pet' => 'hamster'],
(object)['person' => (object)['name' => 'Bob', 'job' => 'Bus conductor'], 'pet' => ''],
];
var_dump(ofilter($a, ['pet' => 'hamster', 'size']));