4

I have this PHP function that works well at searching a multidimensional associative array using a key value pair. I would now like to extend it to search for an array where the key value pair has an SQL-like construct similar to this: name = '%john%'.

function search($array, $key, $value)
{
    $results = array();
    like_search_r($array, $key, $value, $results);
    return $results[0];
}

function like_search_r($array, $key, $value, &$results)
{
    if (!is_array($array)) {
        return;
    }

    if (isset($array[$key]) && $array[$key] == $value) {
        $results[] = $array;
    }

    foreach ($array as $subarray) {
        like_search_r($subarray, $key, $value, $results);
    }
}

I found a good example here filter values from an array similar to SQL LIKE '%search%' using PHP but it only searches a one-dimensional array. The key in this example is preg_grep but I have not figured how to use it in a multidimensional associative array. Any help is appreciated.

EDITED: I would love to be able to pass an array of key values pairs to do one sql like filter. The original requirements remain the same. Filter must support something similar to sql '%like%', no input is required, return the root array when a combination of matches are met. If a key/value pair does not match ignore and move on to the next key/value pair. My input array looks like this:

     array('FIRST_NAME'=>'ma','MIDDLE_NAME'=>'bill',
     'LAST_NAME'=>'jo','ALIASES'=>'phil',
     'DOB'=>'2017-07-05','COUNTRY_OF_BIRTH'=>'Jamaica',
     'Countries1'=>array(array('COUNTRY_CODE'=>'JM'),array('COUNTRY_CODE'=>'AL')),
     'Countries2'=>array(array('COUNTRY_CODE'=>'JM'),array('COUNTRY_CODE'=>'AL')));

A sample array to be filtered can be found here: https://www.tehplayground.com/dIMKbb6Tcw5YU38R

1 Answer 1

3
+50

I think the way to go is with preg_match():

function match($search, $subject)
{
    $search = str_replace('/', '\\/', $search);

    return preg_match("/$search/i", (string)$subject);
}

function like_search_r($array, $key, $value, array &$results = [])
{
    if (!is_array($array)) {
        return;
    }

    $key   = (string)$key;
    $value = (string)$value;

    foreach ($array as $arrayKey => $arrayValue) {
        if (match($key, $arrayKey) && match($value, $arrayValue)) {
            // add array if we have a match
            $results[] = $array;
        }

        if (is_array($arrayValue)) {
            // only do recursion on arrays
            like_search_r($arrayValue, $key, $value, $results);
        }
    }
}

$array1 = [
    'foo'    => 'bar',
    'subarr' => [
        'test'                 => 'val',
        'dangerous/characters' => 1,
    ],
];

$results1 = [];
like_search_r($array1, 'fo', 'bar', $results1);
print_r($results1);

/*
Array
(
    [0] => Array
        (
            [foo] => bar
            [subarr] => Array
                (
                    [test] => val
                    [dangerous/characters] => 1
                )

        )

)
*/

$results2 = [];
like_search_r($array1, 'est', 'val', $results2);
print_r($results2);

/*
Array
(
    [0] => Array
        (
            [test] => val
            [dangerous/characters] => 1
        )

)
*/

$results3 = [];
like_search_r($array1, 's/c', 1, $results3);
print_r($results3);

/*
Array
(
    [0] => Array
        (
            [test] => val
            [dangerous/characters] => 1
        )

)
*/

Adjusted after your comment:

function match($search, $subject) { /* no change */ }

function like_search_r($array, $key, $value, array &$results = [], $level = 0)
{
    if (!is_array($array)) {
        return false;
    }

    $key   = (string)$key;
    $value = (string)$value;

    $found = false;

    foreach ($array as $arrayKey => $arrayValue) {
        if (match($key, $arrayKey) && match($value, $arrayValue)) {
            return true;
        }

        if (is_array($arrayValue)) {
            // only do recursion on arrays
            // results are only added on top level
            if (like_search_r($arrayValue, $key, $value, $results, $level+1)) {
                if ($level == 1) {
                    $results[] = $array;
                }
                $found = true;
            }
        }
    }

    return $found;
}

$array2   = [['id' => 0, 'values' => ['name' => 'bill']], ['id' => 1, 'values' => ['name' => 'john']]];
$results4 = [];
like_search_r($array2, 'name', 'john', $results4);
print_r($results4);

/*
Array
(
    [0] => Array
        (
            [id] => 1
            [values] => Array
                (
                    [name] => john
                )

        )

)
*/
Sign up to request clarification or add additional context in comments.

13 Comments

It does work but the only issue is that it is case sensitive. So so if the value stored is "Mark" and I search for "ma" I do not get a result. If I, however, search for "Ma" I do get a result. Also, the third test did not work for me but it does not present an important use case for me. Make it case insensitive and I will give you the 50 points.
I've changed it, to be case-insensitive. It's just an i after the match-pattern.
I tried to award you but it says I have to wait 13 hours.
Is there a way to have it return the array from the top node array instead of the sub array? for example array( [0]=>[[id]=>0,values['name']=>'bill'], [1]=>[[id]=>1,values['name'=>'john']]); If search for name = john, I would love to get not just the values array but from the root node [1]. Is this possible?
If it could return from the root node as stated above it would be perfect for my use case.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.