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.

Gordon
  • 312,688
  • 75
  • 539
  • 559
Codecube
  • 59
  • 7
  • 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. – Gordon Jan 31 '13 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? – nice ass Jan 31 '13 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. – Codecube Jan 31 '13 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. – Codecube Jan 31 '13 at 21:54

1 Answers1

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.

Gordon
  • 312,688
  • 75
  • 539
  • 559
  • 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) { – Codecube Jan 31 '13 at 22:16
  • 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 Jan 31 '13 at 22:19
  • 1
    @Codecube see here http://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). – Gordon Jan 31 '13 at 23:11
  • 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 Jan 31 '13 at 23:24
  • 1
    @Codecube I've added an explanation. You can also see a demo at http://eval.in/8141 showing that the code works as given. If you have to use `==` you are likely not passing in integers for $id. – Gordon Feb 01 '13 at 08:08
  • @Gordon, thank you, yeah i checked my values were strings, i have converted them integers and it now works 100%, and im understanding how it works... Now to ask you another question, how can i work back? Like if i pass the id of 2, i want it to find its parent id receptively until it reaches the root? – Codecube Feb 02 '13 at 00:33
  • @Codecube that would be another question to ask on SO (after you have searched the existing questions of course) ;) – Gordon Feb 02 '13 at 09:20