note 126183 deleted from function.array-search by crell

From: Date: Thu, 31 Mar 2022 20:47:36 +0000
Subject: note 126183 deleted from function.array-search by crell
References: 1  Groups: php.notes 
Request: Send a blank email to php-notes+get-217967@lists.php.net to get a copy of this message
Note Submitter: vikingjs+phpnet at mac dot com ---- An implementation of a search function that uses a callback, to allow searching for objects of arbitrary complexity: <?php function array_usearch(array $array, Closure $test) { $found = false; $iterator = new ArrayIterator($array); while ($found === false && $iterator->valid()) { if ($test($iterator->current())) { $found = $iterator->key(); } $iterator->next(); } return $found; } ?> The above takes a closure, rather than a callable, but that could easily be altered. Like array_search, this will return false if no match is found. I made the closure the last parameter in case php one day implements trailing-closure syntax. For instance, if you have an array of objects with an id property, you could search for the object with a specific id like this: <?php class Thing { public $id; public $name; public function __construct($id, $name, $category) { $this->id = $id; $this->name = $name; } } $listOfThings = [ new Thing(1, 'one'), new Thing(2, 'two'), new Thing(3, 'three'), new Thing(4, 'four'), ]; $id4Index = array_usearch($listOfThings, function($thing) { return $thing->id === 4; }); ?> Since the Closure can capture information, it becomes simple to write a function that takes search criteria as parameters, creates a closure with those criteria, and calls the above. A simple example that does the same search as above would be <?php $idClosure = function($id) { return function($item) use ($id) { return $item->id = $id; } } $id4Index = array_usearch($idClosure(4)); ?> It's more complex to set up, but now any search for an id can be written in a single line, making things clean and concise. For a more complex example, this function takes an array of key/value pairs and returns the key for the first item in the array that has all those properties with the same values. <?php function firstIndexMatching(array $array, array $criteria, bool $useStrict = true) { if (count($criteria) < 1) { return false; } // create a closure that has captured the search criteria $testWithCriteria = function($criteria, $useStrict) { return function($item) use ($criteria, $useStrict) { foreach($criteria as $key => $value) { if (!isset($item->$key)) { return false; } else if ($useStrict && $item->$key !== $value) { return false; } else if (!$useStrict && $item->$key != $value) { return false; } } return true; }; }; return array_usearch($array, $testWithCriteria($criteria, $useStrict)); } ?> If you have a list of people, for instance, you can now simply find the person record with a specific first and last name: <?php $joeSchlabotnikIndex = firstIndexMatching($people, [ 'firstName' => 'Joe', 'lastName' => 'Schlabotnik' ]); ?> Obviously firstIndexMatching could be modified to use case-insensitive comparisons or anything else that fits your use-case. They idea is capturing the input data. The final step is a function that returns the item, rather than its key, or null if no match found: <?php function firstItemMatching(array $array, array $criteria, bool $useStrict = true) { // fun fact: $array[false] is equivalent to $array[0] $index = firstIndexMatching($array, $criteria, $useStrict); return $index !== false ? $array[$index] ?? null : null; } ?>

« previous php.notes (#217967) next »