I want to write a search function that can recursively search an array and narrow down the results on each iteration. The idea is that it finds a first set of results, then continues with that result and searches it again to find the next set of results. The search criteria is provided as an array with needles. I have already a basic function going, but results are not as expected.

I have written the recursive function which is working. However the iterations does not give me my final result.

$array = array(
    0 => array('id' => 1, 'currency' => 'EUR', 'name' => 'NL', 'country_code' => 31), 
    1 => array('id' => 2, 'currency' => 'EUR', 'name' => 'DE', 'country_code' => 49),
    2 => array('id' => 3, 'currency' => 'EUR', 'name' => 'FR', 'country_code' => 33),
    3 => array('id' => 4, 'currency' => 'EUR', 'name' => 'BE', 'country_code' => 32),
    4 => array('id' => 5, 'currency' => 'USD', 'name' => 'US', 'country_code' => 1),
    5 => array('id' => 6, 'currency' => 'Rand', 'name' => 'SAF', 'country_code' => 27),
    6 => array('id' => 7, 'currency' => 'Rubbles', 'name' => 'RUS', 'country_code' => 7),
    7 => array('id' => 8, 'currency' => 'EUR', 'name' => 'IT', 'country_code' => 39),
    8 => array('id' => 9, 'currency' => 'Pound', 'name' => 'GB', 'country_code' => 44),
); //list of countries which needs to searched

$query = array('currency' => 'EUR', 'id' => 8, 'country_code' => 49); //search needles

function _searchData(array $array, array $query, $counter=0) 
{
  $result = array();
  $matches_found = array();

  if($array){
     $i = $counter; //set counter, start with 0
     $keys = array_keys($query); //get query keys
     $vals = array_values($query); //get query values
     $max = count($keys); //set max_count to limit iteration

     foreach($array as $arrkey => $arrval){
       if(isset($arrval[$keys[$i]]) && $arrval[$keys[$i]] == $vals[$i] && $i < $max){ 
         $matches_found = $this->_searchData($arrval, $query, $i++); //return result, increment counter
         if($matches_found){    
           $arrval = $matches_found; //overwrite result array           
          }
       $result[] = $arrval;  
     }
   }
  return $result;
}
Expected result = 
**it should fail on the 3rd iteration because "country_code" does not match 
on result id 8.  

I think my main problem is with the counter, as it needs to be increased. Within the foreach loop I am not able to get full control. I have tries storing matches_found and re-use it outside the loop, but that does not work either.

The goal of the function is to narrow down the result on each iteration to end with a final set of result as it matches fewer key value pairs. Ps maybe there is an easier way to search an array with a large set of needles, and i am always open for other suggestions, but my main goal is to get this recursive function going as it makes the most sense to me to achieve my desired results. Also in terms of flexibility as I can have as many search needles as wanted. I hope someone can set me in the right direction. Thanks in advance!

1 Answers

2
Yoshi On Best Solutions

I think you can do a lot simpler given your requirements, e.g.:

<?php

$in = [
    ['id' => 1, 'currency' => 'EUR',     'name' => 'NL'],
    ['id' => 2, 'currency' => 'EUR',     'name' => 'DE'],
    ['id' => 3, 'currency' => 'EUR',     'name' => 'FR'],
    ['id' => 4, 'currency' => 'EUR',     'name' => 'BE'],
    ['id' => 5, 'currency' => 'USD',     'name' => 'US'],
    ['id' => 6, 'currency' => 'Rand',    'name' => 'SAF'],
    ['id' => 7, 'currency' => 'Rubbles', 'name' => 'RUS'],
    ['id' => 8, 'currency' => 'EUR',     'name' => 'IT'],
    ['id' => 9, 'currency' => 'Pound',   'name' => 'GB'],
];

$query = ['currency' => 'EUR', 'id' => 8]; //search needles

// array_filter will use the given callback to check every entry
$out = array_filter($in, function(array $candidate) use ($query) {
    // iterate over query
    foreach ($query as $key => $predicate) {
        // if the key exists, but the value differs
        if (isset($candidate[$key]) && $candidate[$key] !== $predicate) {
            // it should not be included
            return false;
        }
    }

    // otherwise, include
    return true;
});

print_r($out);

demo: https://3v4l.org/aAZPP

ref: https://www.php.net/manual/function.array-filter.php