Reputation: 15616
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.
Upvotes: 1
Views: 355
Reputation: 47864
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...
Upvotes: 0
Reputation: 1757
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
Upvotes: 1
Reputation: 3201
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
.
Upvotes: 0