I need to unset elements from arrays that are nested into another array, in a way that only the first N
elements would 开发者_StackOverflow社区be kept (N
being predefined). Only elements that have a numerical index should be affected.
Input array:
Array
(
[0] => Array (
[a] => 'w'
[b] => Array (
[0]=> 'x'
[1]=> 'x'
[2]=> 'x'
)
)
[1] => Array (
[a] => 'y'
)
[2] => Array (
[0] => 'z'
[1] => 'z'
[2] => 'z'
)
)
Desired output (with N=2
):
Array
(
[0] => Array (
[a] => 'w'
[b] => Array (
[0]=> 'x'
[1]=> 'x'
)
)
[1] => Array (
[a] => 'y'
)
)
Based on the above definition, only [0][b][2]
and [2]
got unset because they had a numerical index and because they both represnted the 3rd element of their respective array.
Haven't tested but something like this might work.
function myFunc(&$array){
foreach($array as $key=>&$value){
if(is_array($value)){
myFunc($value);
}
if(is_numeric($key) && $key > 1){
unset($array[$key]);
}
}
}
About array_walk. php.net says:
the programmer cannot add, unset or reorder elements. If the callback does not respect this requirement, the behavior of this function is undefined, and unpredictable.
Write yourself a function that does exactly what you want. Then document the function so if you need to use it in about two weeks, you might want to know what exactly that function is doing.
I say this because the data structure you want to handle seems to be very specific. So it's worth to encapsulate it in a function on it's own to hide away the complexity. Name the function properly.
Inside the function you can process the data in the various ways and with the various conditions you need to formulate. Parameters from the outside can be passed as function parameters. The return value is the result you aim to achieve.
This worked for me, perhaps not the cleanest code.
$array = array
(
array(
'a' => 'w',
'b' => array('x','x','x')
),
array(
'a' => 'y'
),
array(
'z','z','z'
)
);
function recurse_and_strip ($array, &$size=2)
{
foreach ($array as $key => &$element)
{
if (is_hash($element))
{
$element = recurse_and_strip($element,$size);
} else if (is_array($element))
{
$deletefrom = false;
// now the tricky part.. see how many must be deleted
for ($i=0; $i < count($element); $i++ )
{
if ($size == 0)
{
echo "Delete from " . $i;
$deletefrom = $i;
break 1;
}
$size--;
}
if ($deletefrom !== false)
{
if ($deletefrom == 0)
{
unset($array[$key]);
} else {
array_splice($element,$deletefrom);
}
}
}
}
return $array;
}
// source http://www.benjaminkeen.com/?p=23
function is_hash($var)
{
if (!is_array($var))
return false;
return array_keys($var) !== range(0,sizeof($var)-1);
}
var_dump(recurse_and_strip($array,2));
array_walk_recursive
itself cannot achieve what you want. Even though you can pass array by reference, unsetting the variable in the callback will only unset it in that scope.
However, you can use walk_recursive_remove function:
/**
* http://uk1.php.net/array_walk_recursive implementation that is used to remove nodes from the array.
*
* @param array The input array.
* @param callable $callback Function must return boolean value indicating whether to remove the node.
* @return array
*/
function walk_recursive_remove (array $array, callable $callback) {
foreach ($array as $k => $v) {
if (is_array($v)) {
$array[$k] = walk_recursive_remove($v, $callback);
} else {
if ($callback($v, $k)) {
unset($array[$k]);
}
}
}
return $array;
}
You will need to implement your own logic using the $callback
to unset the specific values.
Here's a more general solution to modifying the array to which the leaf belongs. You can unset the current key, or add siblings, etc.
/**
* Modified version of array_walk_recursive that passes in the array to the callback
* The callback can modify the array or value by specifying a reference for the parameter.
*
* @param array The input array.
* @param callable $callback($value, $key, $array)
*/
function array_walk_recursive_array(array &$array, callable $callback) {
foreach ($array as $k => &$v) {
if (is_array($v)) {
array_walk_recursive_array($v, $callback);
} else {
$callback($v, $k, $array);
}
}
}
精彩评论