Reputation: 9838
Really struggling with this. I have a multidimensional array n
levels deep. Each 'array level' has information I need to check (category) and also check if it contains any arrays.
I want to return the category ids of all the arrays which have a category and do not contain an array (i.e. the leaves). I can echo output properly, but I am at a loss as how to return the ids in an array (without referencing)
I have tried RecursiveIteratorIterator::LEAVES_ONLY
and RecursiveArrayIterator
but I don't think they work in my case? (Maybe I am overlooking something)
$array
array(2) {
["1"]=>
string(5) "stuff"
["2"]=>
array(2) {
["category"]=>
string(1) "0"
["1"]=>
array(3) {
[0]=>
array(3) {
["category"]=>
string(1) "1"
["1"]=>
string(5) "stuff"
["2"]=>
string(5) "stuff"
}
[1]=>
array(5) {
["category"]=>
string(1) "2"
["1"]=>
string(5) "stuff"
["2"]=>
string(5) "stuff"
}
[1]=>
array(5) {
["1"]=>
string(5) "stuff"
["32"]=>
string(5) "stuff"
}
}
}
}
My function
public function recurs($array, $cats = [])
{
$array_cat = '';
$has_array = false;
// Check if an id exists in the array
if (array_key_exists('category', $array)) {
$array_cat = $array['category'];
}
// Check if an array exists within the array
foreach ($array as $key => $value) {
if (is_array($value)) {
$has_array = true;
$this->recurs($value, $cats);
}
}
// If a leaf array
if (!$has_array && is_numeric($array_cat)) {
echo "echoing the category here works fine: " . $array_cat . "\n";
return $array_cat;
}
}
Calling it
$cats_array = $this->recurse($array)
Output echoed
echoing the category here works fine: 1
echoing the category here works fine: 2
How do I return the ids in an array to use in the $cats_array
variable?
EDIT: The output should match the echo, so I should get an array containing (1, 2)
since they are the only arrays with categories and no arrays within them
array(2){
[0]=>
int(1) "1"
[1]=>
int(1) "2"
}
Upvotes: 2
Views: 1271
Reputation: 4905
If I understood you correctly this function will do the job:
function getCategories(array $data)
{
if ($subArrays = array_filter($data, 'is_array')) {
return array_reduce(array_map('getCategories', $subArrays), 'array_merge', array());
};
return array_key_exists('category', $data) ? array($data['category']) : array();
}
If the array contains any sub-arrays they will be returned by array_filter and you will enter the if statement. array_map will apply the function recursively to the sub-arrays and array_reduce will merge the results.
If the array doesn't contain any sub-arrays you will not enter the if statement and the function will return an array with the category if it is present.
Note that you might need to use array_unique to return unique categories.
Also for small performance optimization instead of array_key_exists you can use isset($array[$key]) || array_key_exists($key, $array).
Update
If you want to update your function to make it work you have to recursively collect and merge the sub results. In the following example I introduced a new variable for this:
public function recurs($array, $cats = [])
{
$result = [];
$array_cat = '';
$has_array = false;
// Check if an id exists in the array
if (array_key_exists('category', $array)) {
$array_cat = $array['category'];
}
// Check if an array exists within the array
foreach ($array as $key => $value) {
if (is_array($value)) {
$has_array = true;
$result = array_merge($result, $this->recurs($value, $cats));
}
}
// If a leaf array
if (!$has_array && is_numeric($array_cat)) {
echo "echoing the category here works fine: " . $array_cat . "\n";
return [$array_cat];
}
return $result;
}
Upvotes: 3