1

I have an array with values like:

$menuArray = [
    [
        'parent' => 'Basic',
        'parentId' => 1,
        'child' => 'Birthday',
        'childId' => 2,
    ],
    [
        'parent' => 'Basic',
        'parentId' => 1,
        'child' => 'Gender',
        'childId' => 3,
    ],
    [
        'parent' => 'Geo',
        'parentId' => 10,
        'child' => 'Current City',
        'childId' => 11,
    ],
    [
        'parent' => 'Known me',
        'parentId' => 5,
        'child' => 'My personality',
        'childId' => 7,
    ],
    [
        'parent' => 'Known me',
        'parentId' => 5,
        'child' => 'Best life moment',
        'childId' => 8,
    ],
];

And I want to group this array on parent (or parentId) and the final result would be like:

[
    [
        'parent' => 'Basic',
        'parentId' => 1,
        'child' => ['Birthday', 'Gender'],
    ],
    [
        'parent' => 'Geo',
        'parentId' => 10,
        'child' => ['Current City'],
    ],
    [
        'parent' => 'Known me',
        'parentId' => 5,
        'child' => ['My personality', 'Best life moment'],
    ],
]

My current code:

$filter = [];
$f = 0;
for ($i = 0; $i < count($menuArray); $i++) {
    $c = 0;
    for ($b = 0; $b < count($filter); $b++) {
        if ($filter[$b]['parent'] == $menuArray[$i]['parent']) {
            $c++;
        }
    }
    if ($c == 0) {
        $filter[$f]['parent'] = $menuArray[$i]['parent'];
        $filter[$f]['parentId'] = $menuArray[$i]['parentId'];
        $filter[$f]['child'][] = $menuArray[$i]['child'];
        $f++;
    } 
}

But it doesn't push child values from subsequent encounters of a parent-related row into the result array.

1
  • Direct answer to your question is you're not adding children when you've found a matching parent already defined. As well as saying c++ you should also say filter[$b]['child'][] = $menuArray[$i]['child'] Commented Jun 2, 2010 at 1:51

3 Answers 3

1

Try:

$filter = array();
foreach ($menuArray as $menu) {
  if (!array_key_exists($menu['parent_id'], $filter)) {
    $filter[$menu['parent_id']] = array(
      'parent' => $menu['parent'],
      'parent_id' => $menu['parent_id'],
      'child' => array()
    );
  }
  $filter[$menu['parent_id']]['child'][$menu['child_id']] = $menu['child'];
}

This will produce an array like:

Array
(
    [1] => Array
        (
            [parent] => Basic
            [parentId] => 1
            [child] => Array
                (
                    [2] => Birthday
                    [3] => Gender
                )

        )

    [10] => Array
        (
            [parent] => Geo
            [parentId] => 10
            [child] => Array
                (
                    [11] => Current City                  
                )

        )

    [5] => Array
        (
            [parent] => Known me
            [parentId] => 5
            [child] => Array
                (
                    [7] => My personality
                    [8] => Best life moment
                )

        )
)

Notice that the array indexes match the IDs. You can't loop this with a for loop but you can foreach ($filter as $parent_id=>$parent) correctly. If you want to you can change line 4 of my code to $filter['key_' . $menu['parent_id']] to force a string and a for loop will work

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

Comments

0

The next piece of code is completely untested, and based on the idea that it's sorted by parentId.

$filter = array();
$option = null;
for( $i = 0; $i < count( $menuArray ); $i++ ) {
    if( count( $filter ) < 1 || $filter[count($filter)-1]['parentId'] != $menuArray['parentId'] ) {
        if( $option != null ) {
            $filter[]   = $option;
        }

        $option = array(
            "parent"    => $menuArray[$i]['parent'],
            "parentId"  => $menuArray[$i]['parentId'],
            "child"     => array()
        );
    }

    $option['child'][]  = $menuArray[$i]['child'];
}

$filter[]   = $option; // one last time, because we left the loop already.
unset( $option ); // we don't need it anymore.

What it does, it creates a $option for every parent. As soon as we hit the next parentId we add the current $option to the $filter array and create a new $option object.

All the children just keep getting added to the current $option.

Comments

0
  • Iterate the input array only once.

  • If encountering a parentId for the first time, mutate the row by removing the childId elemement and cast the child value as an array -- which will convert the scalar value to a single-element array containing the value.

    Then push the mutated row into the result array as an associative multidimensional entry so that subsequent encounters of the parentId can be acknowledged.

  • If encountering a parentId after the first time, merely push the child value into its respective group in the result array.

Code: (Demo)

$result = [];
foreach ($array as $row) {
    if (!isset($result[$row['parentId']])) {
        unset($row['childId']);
        $row['child'] = (array) $row['child'];
        $result[$row['parentId']] = $row;
    } else {
        $result[$row['parentId']]['child'][] = $row['child'];
    }
}
var_export(array_values($result));

If you prefer to not mutate the (copied value of) $row with function calls, you can manually type out the desired structure. (Demo)

    // if (...) {
        $result[$row['parentId']] = [
            'parent' => $row['parent'],
            'parentId' => $row['parentId'],
            'child' => [$row['child']]
        ];
    // } else...

Comments

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.