In the hopes that they will be useful to anyone else, I've decided to share the pair of functions that I came up with.
If you have time to test these functions, please give me some ideas for what needs improvement.
Code: Select all
<?php
/**
* Analyzes an array and generates for each non-array element a list of the keys
* used to navigate the hierarchy to get to that element.<br />
*
* @param array $input
* The array to trace should be uniform (have equal depth on all branches).
*
* @param array $output
* This by-reference, three-dimensional array describes all non-array elements
* of the input array.<br />
* <br />
* Structure of the resultant $output array:<br />
* <ul>
* <li>[0] First non-array element in order of discovery:
* <ul>
* <li>[0] Element content</li>
* <li>[1] List of ancestor keys:
* <ul>
* <li>[0] Root ancestor key</li>
* <li>...</li>
* <li>[n-1] Grand-parent key</li>
* <li>[n] Parent key</li>
* </ul>
* </li>
* </ul>
* </li>
* <li>[1] Second non-array element:
* <ul>
* <li>[0] Element content</li>
* <li>[1] List of ancestor keys:
* <ul>
* <li>[0] Root ancestor key</li>
* <li>...</li>
* <li>[n-1] Grand-parent key</li>
* <li>[n] Parent key</li>
* </ul>
* </li>
* </ul>
* </li>
* <li>...</li>
* </ul>
*
* @return array
* The returned array is always empty (except for recursive calls).
*/
function traceArray (array $input, array &$output) {
$trace = (func_num_args() > 2) ? func_get_arg(2) : array();
if (! is_array($trace)) {
$trace = array();
}
foreach ($input as $n => $node) {
$trace[] = $n;
if (is_array($node)) {
$self = __FUNCTION__;
$trace = $self($node, $output, $trace); // Recursive call
} else {
$output[] = array($node, $trace);
array_pop($trace);
}
}
array_pop($trace);
return $trace;
}
/**
* Arranges the hierarchy of keys in a multidimensional array according to the
* sequence of integers in the $order array.<br />
*
* @param array $input
* The array to rekey should be uniform (have equal depth on all branches).
*
* @param array $order
* This one-dimensional array of integers determines the new key sequence. The
* maximum integer in this array should be less than the depth of the shallowest
* non-array element in the $input array. The number of elements in this array
* should be less than or equal to that depth.
*
* @return array
* Returns the rekeyed array or an empty array if $order is invalid.
*/
function rekeyArray (array $input, array $order) {
$output = array();
$order = preg_grep('/^\d+$/', $order); // Prevents "eval" code injection
if (count($order) == 0) {
return $output;
}
$trace = array();
traceArray($input, $trace);
$count = count($trace);
for ($t = 0; $t < $count; $t++) {
$keys = '[ $trace[$t][1]['.implode('] ][ $trace[$t][1][', $order).'] ]';
eval('$output'.$keys.' = $trace[$t][0];');
}
return $output;
}Code: Select all
$files = array
( 'images' => array
( 'name' => array
( 0 => 'a.png'
, 1 => 'b.gif'
)
, 'type' => array
( 0 => 'image/png'
, 1 => 'image/gif'
)
, 'tmp_name' => array
( 0 => '/tmp/php0001.tmp'
, 1 => '/tmp/php0002.tmp'
)
, 'error' => array
( 0 => 0
, 1 => 0
)
, 'size' => array
( 0 => 100
, 1 => 200
)
)
);
print_r(rekeyArray($files, array(2, 1)));
/*
Array
(
[0] => Array
(
[name] => a.png
[type] => image/png
[tmp_name] => /tmp/php0001.tmp
[error] => 0
[size] => 100
)
[1] => Array
(
[name] => b.gif
[type] => image/gif
[tmp_name] => /tmp/php0002.tmp
[error] => 0
[size] => 200
)
)
*/