3

Let’s say I have two arrays, one is of keys I require, the other is an array I want to test against.

In the array of keys I require, each key might have a value which is itself an array of keys I require, and so on.

Here’s the function so far:

static function keys_exist_in_array(Array $required_keys, Array $values_array, &$error)
{       
    foreach($required_keys as $key)
    {
        //  Check required key is set in the values array
        //          
        if (! isset($values_array[$key]))
        {
            //  Required key is not set in the values array, set error and return
            //
            $error = new Error();

            return false;
        }

        //  Check the value is an array and perform function on its elements
        //
        if (is_array($values_array[$key]))
        {
            Static::keys_exist_in_array($required_keys[$key], $values_array[$key], $error);
        }

        return true;
    }
}

My problem is that the array I want to submit to $required_keys CAN look like this:

$required_keys = array(
    ‘key1’,
    ‘key2’,
    ‘key3’,
    ‘key4’ = array(
        ‘key1’,
        ‘key2’,
        ‘key3’ = array(
            ‘key1’
        )
    )
);

Obviously the problem here is that foreach only finds each key, e.g. ‘key4’, rather than the values without their own value, e.g. ‘key1’, ‘key2’, ‘key3’.

But if I loop through with a standard for loop, I only get the values, key1, key2, key3.

What’s a better way of doing this?

Thanks

Adam Carter
  • 4,741
  • 5
  • 42
  • 103
  • 1
    When looping check if the value is an array and if so call the function again inside the loop and pass the value (array). – AbraCadaver Feb 06 '15 at 00:30
  • check the type of element, if it's an array, recurse on that element! – taesu Feb 06 '15 at 00:30
  • http://stackoverflow.com/questions/2648968/what-is-a-recursive-function-in-php – Capsule Feb 06 '15 at 00:31
  • As stated above, the problem is with the looping. Foreach only gives keys, for only gives values. I need both in one loop… Lines 18-21 are the recursive function – Adam Carter Feb 06 '15 at 00:31
  • Why do you need both? `key1`, `key2`, etc. are all values. Your array is strange: why is it a mix of an indexed and associative arrays? – Barmar Feb 06 '15 at 00:40
  • `foreach ($array as $element)` just loops over the values. `foreach($array as $key => $element)` loops over the key and values. But it's not clear which you want in your array. Why do you call the values keys? – Barmar Feb 06 '15 at 00:41

3 Answers3

2

Several problems:

$key is the element of the array, not a key, so you

You shouldn't return false as soon as you see a non-matching element, because there could be a matching element later in the array. Instead, you should return true as soon as you find a match. Once you find a match, you don't need to keep searching.

You need to do the isarray() test first, because you'll get an error if $key is an array and you try to use $values_array[$key]. And it should be isarray($key), not isarray($values_array[$key]).

You need to test the value of the recursive call. If it succeeded, you can return immediately.

You should only return false after you finish the loop and don't find anything.

static function keys_exist_in_array(Array $required_keys, Array $values_array, &$error)
{       
    foreach($required_keys as $key)
    {
        //  Check the value is an array and perform function on its elements
        //
        if (is_array($key))
        {
            $result = Static::keys_exist_in_array($required_keys[$key], $values_array[$key], $error);
            if ($result) {
                return true;
            }
        }            
        //  Check required key is set in the values array
        //          
        elseif (isset($values_array[$key]))
        {
            return true;
        }
    }
    $error = new Error();
    return false;
}
Barmar
  • 741,623
  • 53
  • 500
  • 612
1

Convert the array to a key => value array with an empty value for the "keys" that don't have a value.

$arr = [
    'a',
    'b' => ['foo' => 1, 'bar' => 'X'],
    'c' => ['baz' => 'Z'],
    'd'
];

$res = [];
$keys = array_keys($arr);
$vals = array_values($arr);
foreach ($vals as $i => $v) {
    if (is_array($v)) {
        $res[$keys[$i]] = $v;
    } else {
        $res[$v] = [];
    }
}

print_r($res);

Result:

Array
(
    [a] => Array
        (
        )

    [b] => Array
        (
            [foo] => 1
            [bar] => X
        )

    [c] => Array
        (
            [baz] => Z
        )

    [d] => Array
        (
        )
)
gregjor
  • 21,802
  • 1
  • 23
  • 16
0

Returning false here is the correct action if you require that ALL the $required_keys exist, because you want it to stop looking as soon as it finds a missing key.

static function keys_exist_in_array(Array $required_keys, Array $values_array, &$error)
{       
    foreach($required_keys as $key=>$value)
    {
        //check if this value is an array
        if (is_array($value))
        {
            if (!array_key_exists($key, $values_array) 
                || !Static::keys_exist_in_array($value, $values_array[$key], $error);){
                $error = new Error();
                return false;
            }


        }

        //  Since this value is not an array, it actually represents a 
        //   key we need in the values array, so check if it is set 
        //   in the values array    
        elseif (!array_key_exists($value, $values_array))
        {
            //  Required key is not set in the values array, set error and return
            $error = new Error();
            return false;
        }


    }
    //All elements have been found, return true
    return true;
}
CragMonkey
  • 808
  • 1
  • 11
  • 22