I've a list of associative arrays as below:
[
"country" => "AU",
"state" => "VIC",
"suburb" => "Carlton",
"precedence" => ["country", "state", "suburb"]
]
And I want a new multidimensional array like below where the elements are nested based on the order defined by precedence key on first array:
[
"country" => [
"AU" => [
"state" => [
"VIC" => [
"suburb" => "Carlton
]
]
]
]
]
The above is just an example and I want a generic solution that will work for any kinds of array. Only 1 condition that'll be satisfied by all input arrays is that they'll have a precedence element denoting the order in which the output array needs to be generated.
I've tried some recursive solution but it's not working as expected and I've got PHP Fatal error: Allowed memory size of 1073741824 bytes exhausted (looks like it's running infinitely):
function generateArray(&$array)
{
foreach ($array['precedence'] as $key => $property) {
if ($key == sizeof($array['precedence']) - 1) {
return [$property => $array[$property]];
} else {
return generateAssetConfig($array);
}
}
}
CodePudding user response:
Try this:
function generateNestedArray($arr) {
$precedence = $arr['precedence'];
$nestedArray = [];
for ($i = count($precedence)-1; $i >= 0; $i--) {
$key = $precedence[$i];
if (!$nestedArray) {
$nestedArray[$key] = $arr[$key];
} else {
$nestedArray = [$key => [ $arr[$key]=> $nestedArray]];
}
}
return $nestedArray;
}
CodePudding user response:
Here's a recursive algorithm to do this:
<?php
$raw = [
[
"country" => "AU",
"state" => "VIC",
"suburb" => "Carlton",
"precedence" => ["country", "state", "suburb"]
],
[
"country" => "AU",
"state" => "NSW",
"suburb" => "Sydney",
"precedence" => ["country", "state", "suburb"]
]
];
function generateFromPrecedence($array)
{
if (!isset($array['precedence']))
throw new Exception('Precedence array does not exist');
if (!empty(array_diff($array['precedence'], array_diff(array_keys($array), ['precedence']))))
throw new Exception('Keys and precendence keys different');
return generateStructure($array);
}
function generateStructure($array, $precedence = 0)
{
if ($precedence == count($array['precedence'])-1)
return [$array['precedence'][$precedence] => $array[$array['precedence'][$precedence]]];
return [$array['precedence'][$precedence] => [$array[$array['precedence'][$precedence]] => generateStructure($array, $precedence)]];
}
$output = generateFromPrecedence($raw[0]);
var_dump($output);
Outputs:
array(1) {
["country"]=>
array(1) {
["AU"]=>
array(1) {
["state"]=>
array(1) {
["NSW"]=>
array(1) {
["suburb"]=>
string(6) "Sydney"
}
}
}
}
}
CodePudding user response:
Simplest solution (recursive function):
function generateArrayRecursion($array, $precedenceIndex = 0) {
$precedence = $array['precedence'];
return [
$precedence[$precedenceIndex] => $precedenceIndex === \count($precedence) - 1
? $array[$precedence[$precedenceIndex]]
: [$array[$precedence[$precedenceIndex]] => generateArrayRecursion($array, $precedenceIndex 1)]
];
}
Alternative solution (loop and array references):
function generateArray($array) {
$precedence = $array['precedence'];
$result = [];
$lastKey = $precedence[count($precedence) - 1];
$currentElement = &$result;
foreach ($precedence as $key) {
if ($key === $lastKey) {
$currentElement[$key] = $array[$key];
} else {
$currentElement[$key] = [$array[$key] => []];
$currentElement = &$currentElement[$key][$array[$key]];
}
}
return $result;
}
Usage example:
$array = [
"country" => "AU",
"state" => "VIC",
"suburb" => "Carlton",
"precedence" => ["country", "state", "suburb"]
];
var_dump(generateArrayRecursion($array));
var_dump(generateArray($array));
CodePudding user response:
You could loop the reversed items of the precedence part.
If there are no items in the result array yet, add the first key => value pair.
Else wrap the current result in a multidimensional array, setting the current value if the iteration as the outer key, and wrap the value (for that key in the source array) together with the current result in a second array.
$source = [
"country" => "AU",
"state" => "VIC",
"suburb" => "Carlton",
"precedence" => ["country", "state", "suburb"]
];
function generateArray($array)
{
$result = [];
foreach(array_reverse($array["precedence"]) as $v) {
$result =! $result ? [$v => $array[$v]] : [$v => [$array[$v] => $result]];
}
return $result;
}
var_export(generateArray($source));
Output
array (
'country' =>
array (
'AU' =>
array (
'state' =>
array (
'VIC' =>
array (
'suburb' => 'Carlton',
),
),
),
),
)