suppose I have a multidimensional array structure:
array(parent)
array
array
array(parent)
array(parent)
...some key I'm looking for....
array
array
array
array
array
array
array
I iterate over it recursively to find an array that contains some key I'm looking for - this is no problem.
But then I need to walk up the tree and add additional key to all parents (marked by parent). I can't wrap my head around it. I can easily walk down the tree recursively but I can't figure out how to walk up. Can someone point me to the right direction?
This is an example that I've just wrote just to get the idea.
NOTE that this will break execution on the first occurrence of the matched value.
codepad link
$arr = array(
array('a','b','c','d'),
array(61,62,63,64,65),
array('e','f','g','h'),
array(6,7,8,9),
array(1,2,array(1,2,3,4,5,6,7,8),4,5,6,7,8),
array('i','j','k','l','m'),
);
function array_walkup( $desired_value, array $array, array $keys=array() ) {
if (in_array($desired_value, $array)) {
array_push($keys, array_search($desired_value, $array));
return $keys;
}
foreach($array as $key => $value) {
if (is_array($value)) {
$k = $keys;
$k[] = $key;
if ($find = array_walkup( $desired_value, $value, $k )) {
return $find;
}
}
}
return false;
}
$keys = array_walkup(3, $arr);
echo "3 has been found in \$arr[".implode('][', $keys)."]";
You can use something along these lines in a class:
private $valueFound;
public function recurse($item)
{
if ($item['special_field'] === 'value you are looking for')
{
$this->valueFound = $item['other_field'];
return;
}
if (isset($item['child'])
{
recurse($item['child']);
}
// Use tail-recursion.
// You are at this point past all recursion and are heading back up the tree.
$item['field_to_set'] = $this->valueFound;
}
Related
How i can remove a value from array what is not fully match the letters.
Array code example:
$Array = array(
'Funny',
'funnY',
'Games',
);
How I can unset all values from this array what is 'funny'
I try via unset('funny'); but is not removing the values from array, is removed just if i have 'funny' on array but 'funnY' or 'Funny' not working
Maybe there is some sophisticated solution with array_intersect_key or something which could do this in one line but I assume this approach is more easily read:
function removeCaseInsensitive($array, $toRemove) {
$ret = [];
foreach ($array as $v) {
if (strtolower($v) != strtolower($toRemove))
$ret[] = $v;
}
return $ret;
}
This returns a new array that does not contain any case of $toRemove. If you want to keep the keys than you can do this:
function removeCaseInsensitive($array, $toRemove) {
$keep = [];
foreach ($array as $k => $v) {
if (strtolower($v) != strtolower($toRemove))
$keep[$k] = true;
}
return array_intersect_keys($array, $keep);
}
You can filter out those values with a loose filtering rule:
$array = array_filter($array, function($value) {
return strtolower($value) !== 'funny';
});
I'm trying to remove duplicated entries where value and type are both equal on a multidimensional associative array, but only using recursion and without array_unique. All keys are associative.
I tried this, and I'm getting the same result as the main array. My logic seems to fail me at this late hour.
function rmDuplicates(&$array) {
$uniqueArray = array();
foreach($array as $k=>$v) {
if (is_array($v)) {
$uniqueArray[$k] = rmDuplicates($v);
} else {
if (!in_array($v, $uniqueArray)) {
$uniqueArray[] = $v;
}
}
}
return $uniqueArray;
}
protected static function arrayShuffling($itemsArray)
{
$itemSwitching = array();Switching
$shItem= array();
foreach ($itemArray as $i => $myItem) {
if (!in_array($myItem->_id, $itemSwitching)) {
$itemSwitching[$i]['_id'] = $myItem->_id;
$itemSwitching[$i]['poistion'] = $myItem['details']['move_to_poistion'];
}
foreach ($itemSwitching as $t => $pinPrep) {
if ($event->_id == $pinPrep['_id']) {
$shuffeledItem[$itemSwitching[$t]['poistion']] = $myItem;
unset($itemArray[$i]);
}
}
}
foreach($shItem as $key=>$shffuledItem){
array_splice( $itemArray, $key, 0, array($shffuledItem));
}
}
I have this method that takes an array and then look at $myItem['details']['move_to_poistion'] index to see what position that element should be moved to. After that it takes that specific element out of the array and then uses splice to insert it back to the $myItem['details']['move_to_poistion'] position.
I am worried about too many foreach lops and want to know if some how we can shorten this.
thanks
Example:
$arr = array(array("name"=>"Bob","species"=>"human","children"=>array(array("name"=>"Alice","age"=>10),array("name"=>"Jane","age"=>13)),array("name"=>"Sparky","species"=>"dog")));
print_r($arr);
array_walk_recursive($arr, function($v,$k) {
echo "key: $k\n";
});
The thing here is that I get only the last key, but I have no way to refer where I been, that is to store a particular key and change value after I left the function or change identical placed value in another identical array.
What I would have to get instead of string is an array that would have all keys leading to given value, for example [0,"children",1,"age"].
Edit:
This array is only example. I've asked if there is universal way to iterate nested array in PHP and get full location path not only last key. And I know that there is a way of doing this by creating nested loops reflecting structure of the array. But I repeat: I don't know the array structure in advance.
To solve your problem you will need recursion. The following code will do what you want, it will also find multiple paths if they exists:
$arr = array(
array(
"name"=>"Bob",
"species"=>"human",
"children"=>array(
array(
"name"=>"Alice",
"age"=>10
),
array(
"name"=>"Jane",
"age"=>13
)
),
array(
"name"=>"Sparky",
"species"=>"dog"
)
)
);
function getPaths($array, $search, &$paths, $currentPath = array()) {
foreach ($array as $key => $value) {
if (is_array($value)) {
$currentPath[] = $key;
if (true !== getPaths($value, $search, $paths, $currentPath)) {
array_pop($currentPath);
}
} else {
if ($search == $value) {
$currentPath[] = $key;
$paths[] = $currentPath;
return true;
}
}
}
}
$paths = array();
getPaths($arr, 13, $paths);
print_r($paths);
I have a multidimensional array nested to an unknown/unlimited depth.
I'd like to be able to loop through every element.
I don't want to use, foreach(){foreach(){foreach(){}}} as I don't know the depth.
I'm eventually looking for all nested arrays called "xyz". Has anyone got any suggestions?
I'm eventually looking for all nested arrays called "xyz". Has anyone got any suggestions?
Sure. Building on the suggestions to use some iterators, you can do:
$iterator = new RecursiveIteratorIterator(
new RecursiveArrayIterator($array),
RecursiveIteratorIterator::SELF_FIRST
);
foreach ($iterator as $key => $item) {
if (is_array($item) && $key === 'xyz') {
echo "Found xyz: ";
var_dump($item);
}
}
The important difference between the other answers and this being that the RecursiveIteratorIterator::SELF_FIRST flag is being employed to make the non-leaf (i.e. parent) items (i.e. arrays) visible when iterating.
You could also make use of a ParentIterator around the array iterator, rather than checking for arrays within the loop, to make the latter a little tidier.
Recursion.
Write a function that walks one array; for each element that is also an array, it calls itself; otherwise, when it finds the target string, it returns.
There is a vast difference between unknown and unlimited. However, you can make use of the SPL Iterators instead of using multiple nested foreach loops.
Example:
$array_obj = new RecursiveIteratorIterator(new RecursiveArrayIterator($array));
foreach($array_obj as $key => $value) {
echo $value;
}
Take a look to the RecursiveIteratorIterator interface.
$interface = new RecursiveIteratorIterator( new RecursiveArrayIterator($your_array) );
foreach($interface as $k=>$v) { /* your function*/ }
Using the comments above, I've found the answer:
function findXyz($array){
foreach($array as $foo=>$bar){
if (is_array($bar)){
if ($bar["xyz"]){
echo "<br />The array of xyz has now been found";
print_r($bar['xyz']);
}else{
findXyz($bar);
}
}
}
}
findXyz($myarray);
This loops through all nested arrays and looks for any element who has a sub-array of xyz, as per my original request. array_walk_array and RecursiveIteratorIterator were unable to achieve this.
Have you thought about using array_walk_recursive for this?
Another (slower) approach would be to flatten the array first before performing a search, ie:
$myarray = array('a','b',array(array(array('x'),'y','z')),array(array('p')));
function array_flatten($array,$return)
{
for($x = 0; $x <= count($array); $x++)
{
if(is_array($array[$x]))
{
$return = array_flatten($array[$x],$return);
}
else
{
if($array[$x])
{
$return[] = $array[$x];
}
}
}
return $return;
}
$res = array_flatten($myarray,array());
Or, for a recursive search, see here for an example:
function arrayRecursiveSearch($needle, $haystack, $path=""){
if(!is_array($haystack)){
die("second argument is not array");
}
global $matches;
foreach($haystack as $key=>$value)
{
if(preg_match("/$needle/i", $key)){
$matches[] = array($path . "$key/", "KEY: $key");
}
if(is_array($value)){
$path .= "$key/";
arrayRecursiveSearch($needle, $value, $path);
unset($path);
}else{
if(preg_match("/$needle/i", $value)){
$matches[] = array($path . "$key/", "VALUE: $value");
}
}
}
return $matches;
}
$arr = array("Asia"=>array('rambutan','duku'),
"Australia"=>array('pear','kiwi'),
"Arab"=>array('kurma'));
print_r(arrayRecursiveSearch("ra",$arr));