1

How can i get the ['id'] from all children elements if i pass it an id.

This is my array...

$array = Array
(
    '0' => Array
    (
        'id' => 1,
        'parent_id' => 0,
        'order_pos' => 0,
        'title' => 'Shirts',
        'childs' => Array
        (
            '0' => Array
            (
                'id' => 2,
                'parent_id' => 1,
                'order_pos' => 0,
                'title' => 'Small Shirts',
            )
        )
    ),
    '1' => Array
    (
        'id' => 3,
        'parent_id' => 0,
        'order_pos' => 0,
        'title' => 'Cameras'
    )
);

If i write i function and pass a variable of say id 1 can someone please tell me how i can return a single dimensional array with merely just the id's of all child elements.. For instance.

From the previous array, if i pass the id of 1, i want the function to return 1, 2 as 2 is an id element of a child element. So if i pass it 2, it should only return 2 as it doesnt have any children.

I hope you understand me, thank you if you can help me...

Note, this can be unlimited, meaning each parent category can have unlimited sub categories or children.

4
  • what if there is the same ID on multiple levels, e.g. the children with ID 2 in your example can have another level of children with ID 3. There is also the top level element with ID 3. Can that happen? And what should be the output then. Commented Jan 31, 2013 at 11:14
  • Why is the parent_id key present in the children elements? Can the value of this be different than the ID of the parent element? Commented Jan 31, 2013 at 11:35
  • @Gordon No that cannot happen. The id is unique, the array gets built from a database where the id is the key field. Commented Jan 31, 2013 at 21:53
  • @One Trick Pony It is there because the array is built from a database table, it has id, parent_id, name, so it builds a multidimensional array from a database table. Commented Jan 31, 2013 at 21:54

1 Answer 1

2

There is basically two problems you need to solve:

  1. search the entire array for the given ID to start at.
  2. pluck all the IDs from the children once the ID is found.

This would work:

function findIds(array $array, $id)
{
    $ids = array();
    $iterator = new RecursiveIteratorIterator(
        new RecursiveArrayIterator($array),
        RecursiveIteratorIterator::SELF_FIRST
    );

    foreach ($iterator as $val) {
        if (is_array($val) && isset($val['id']) && $val['id'] === $id) {
            $ids[] = $val['id'];
            if (isset($val['childs'])) {
                array_walk_recursive(
                    $val['childs'],
                    function($val, $key) use (&$ids) {
                        if ($key === 'id') {
                            $ids[] = $val;
                        }
                    }
                );
            }
        }
    }

    return $ids;
}

print_r( findIds($array, 1) ); // [1, 2]
print_r( findIds($array, 2) ); // [2]
print_r( findIds($array, 3) ); // [3]

The Iterators will make your array fully traversable. This means, you can foreach over the entire array like it was a flat one. Normally, it would return only the leaves (1, 0, 0, Shirts, …), but since we gave it the SELF_FIRST option it will also return the arrays holding the leaves. Try putting a var_dump inside the foreach to see.

In other words, this

foreach ($iterator as $val) {

will go over each and every value in the array.

if (is_array($val) && isset($val['id']) && $val['id'] === $id) {

This line will only consider the arrays and check for the ID you passed to the findById function. If it exists, the ID is added to the array that will get returned by the function. So that will solve problem 1: finding where to start.

if (isset($val['childs'])) { 

If the array has an item "childs" (it should be children btw), it will recursively fetch all the IDs from that item and add them to the returned array:

array_walk_recursive(
    $val['childs'],
    function($val, $key) use (&$ids) {
        if ($key === 'id') {
            $ids[] = $val;
        }
    }
);

The array_walk_recursive accepts an array (1st argument) and will pass the value and the key of the leaves to the callback function (2nd argument). The callback function merely checks if the leaf is an ID value and then add it to the return array. As you can see, we are using a reference to the return array. That is because using use ($ids) would create a copy of the array in the closure scope while we want the real array in order to add items to it. And that would solve problem 2: adding all the child IDs.

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

7 Comments

Thanks man, unfortunately it didnt seem to work... I think it might be around this line: if (is_array($val) && isset($val['id']) && $val['id'] === $id) {
I think it works if i remove one equal sign here $val['id'] === $id to $val['id'] == $id... Now it seems to work @gordon by the way, what ist that 3rd equal sign for? I know single is setting, double is comparing, what is 3?
@Codecube see here stackoverflow.com/questions/1117967. It checks for identity, so when you insert "2" instead of 2 for $id it will not find anything because "2" is not the same type as 2 (string vs int).
ahk i understand that... Nice, did not know PHP could do that.. Only 1 more problem, if i put that 3rd equal sign in it wont work.. If i remove it works exactly how i want it to work... Interesting... Also are you able to explain the code you wrote, like explain why you wrote certain lines of code, id hate to just copy and paste, i want to be able to re-write it in the future and understand it....
@Codecube I've added an explanation. You can also see a demo at eval.in/8141 showing that the code works as given. If you have to use == you are likely not passing in integers for $id.
|

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.