Rory
Rory

Reputation: 838

Determine if specific value exists in any level of a multidimensional array

For this nested array:

$status = array(
  "house" => "OK",
  "car" => array(
      "car1" => "OK",
      "car2" => "ERROR"
   ),
   "boat" => "OK"
);

I want to know if a certain value "ERROR" exists at least once in the array. I don't care what key it's associated with, I just want to know if the $status array contains an "ERROR" status anywhere in it.

Is there a cleaner way to do this than just iterating over the elements with nested for loops?

Upvotes: 1

Views: 2785

Answers (2)

ArtisticPhoenix
ArtisticPhoenix

Reputation: 21671

I actually wouldn't use array recursive. It falls short in a few key things you want to do. And while at first it may seem like a "shortcut" to what you want,if fails in a few key ways:

  • you can't return a simple testable value like a boolean true of false from the callback in it.
  • you can't build the $all array with nesting, because the keys that have an array as their value are never passed to the callback. This essentially flattens the array out.
  • it's not portable, or in other words it's not reusable as it's procedural in nature.

That said, honestly I wouldn't build the all array because it's pointless as you already have that array, so there is no point copying it. As I said I don't think you really want to rebuild $all but if you did you couldn't do it with array_walk_recursive even if you wanted to...

Anyway.

$array = ['zero','one', 'two' => ['zoo' => 'foo']];
             
   
function checkRecursive($value, $array){  
    $x = false;
    foreach($array as $v){

        if(is_array($v)) $x = checkRecursive($value, $v); //recursive

        if($v == $value || $x == true) return true;
    }
    return false;
}


echo checkRecursive('one', $array) ? "true\n" : "false\n";
echo checkRecursive('foo', $array) ? "true\n" : "false\n";  
echo checkRecursive('bar', $array) ? "true\n" : "false\n";

prints

true
true
false

Test it

http://sandbox.onlinephpfunctions.com/code/78d0de421475b8e3104376c698027107e9b7e948

UPDATE I see you changed the question and while you say "value" your array says the "key"

$status = array(
  "house" => "OK",
  "car" => array(
      "car1" => "OK",
      "car2" => "OK"
   ),
   "boat" => "OK"
);

Because all the values are the same. So if you really want to find the key instead then just change it to this

function checkRecursive($value, $array){  
    $x = false;
    foreach($array as $k=>$v){
        if(is_array($v)) $x = checkRecursive($value, $v); //recursive
        if((string)$value == (string)$k || $x == true) return true;
    }
    return false;
}

A few things to note when checking for the keys, it's possible they will not always be strings, or that you will check using an unqoted number. In these cases PHP sometimes has a weird way of changing a string into an integer and non-numeric strings will come out as 0. For example "1" = 1 but "one" = 0 when taken as an integer. Therefore if you have an array with a key of 0 and you check for one it will match it because it will turn "one" into a 0.

You can solve this by casting everything to strings. Or by using the === strict type checking. I prefer the string method because checking with === would make 0 != "0" so if you tried finding a key of 0 with "0" it would say no match. This is true of other numbers. That is unless you plan on checking objects with it.

This is still an issue when looking for the "values" but it's less of one, because there you are not dealing with the native integer keys in an array, so there is a greater possibility you wouldn't encounter it.

It all has to do with PHP's loosely typed nature, it's a feature not a bug...

I put this small example togather:

echo '(int)"one": ';
echo (int)"one";
echo "\n";
echo "0 == 'one': " . (0 == "one" ? "true\n" : "false\n");
echo "1 == '1': " . (1 == "1" ? "true\n" : "false\n");

Outputs:

(int)"one": 0
0 == 'one': true
1 == '1': true

http://sandbox.onlinephpfunctions.com/code/df41f55251570a12d9ca693712bcdc785ad62f85

Upvotes: 3

Rémi Sauvat
Rémi Sauvat

Reputation: 111

You could use the function array_walk_recursive() to get all the values with any level of nested array.

https://secure.php.net/manual/en/function.array-walk-recursive.php

<?php
$status = array(
    "house" => "OK",
    "car" => array(
        "car1" => "OK",
        "car2" => "OK"
    ),
    "boat" => "OK"
);

$required = array();
array_walk_recursive($status, function ($value, $key) use (&$required){
    $required[] = $value;
}, $required);
print '<pre>';
print_r($required);
print '</pre>';
?>

Output:

Array
(
    [0] => OK
    [1] => OK
    [2] => OK
    [3] => OK
)

Upvotes: 7

Related Questions