How to Loop Through a Multidimensional Array Without Knowing It's Depth

Is there a way to loop through a multidimensional array without knowing it's depth?

Yes, you can use recursion. Here's an example where you output all the elements in an array:

function printAll($a) {
if (!is_array($a)) {
echo $a, ' ';
return;
}

foreach($a as $v) {
printAll($v);
}
}

$array = array('hello',
array('world',
'!',
array('whats'),
'up'),
array('?'));
printAll($array);

What you should always remember when doing recursion is that you need a base case where you won't go any deeper.

I like to check for the base case before continuing the function. That's a common idiom, but is not strictly necessary. You can just as well check in the foreach loop if you should output or do a recursive call, but I often find the code to be harder to maintain that way.

The "distance" between your current input and the base case is called a variant and is an integer. The variant should be strictly decreasing in every recursive call. The variant in the previous example is the depth of $a. If you don't think about the variant you risk ending up with infinite recursions and eventually the script will die due to a stack overflow. It's not uncommon to document exactly what the variant is in a comment before recursive functions.

Better way to iterate multidimensional array of unknown complexity from API than a dozen foreach loops in PHP

This is what I've come up with. Kindly modify it to meet your need.

$array = json_decode($json, true);

function traverse_some_array($array){
$result = [];
foreach($array['Rows']['Row'] ?? [] as $row){
if( !empty($row['Header']['ColData']) ){
foreach($row['Header']['ColData'] as $coldata){
if( isset($coldata['value'], $coldata['id']) ){
// there is data in both [Header][ColData][value] AND [Header][ColData][id]

// extract the value, id (in this snippet "value": "40000 Sales Income", "id": "31")
$extract_data = [
'value' => $coldata['value'],
'id' => $coldata['id']
];

// the data that immediately follows the "value"/"id" in [Rows][Row][Rows][Row][ColData]
$immediate_coldata = $row['Rows']['Row'] ?? [];

// you can do what ever you want with the results, eg return it or push it to a result array
$result[] = [
'extract_data' => $extract_data,
'immediate_coldata' => $immediate_coldata
];
}else{
// continue traversing the array
$result = array_merge($result, traverse_some_array($row));
}
}
}
}
return $result;
}

print_r(traverse_some_array($array));

Multidimensional Arrays Nested to Unlimited Depth

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.

Iterating over multidimensional array in PHP

You can recursively call the same function if it finds a nested array like this:

$input = array(
'field_5b23d04fef8a6' => '',
'field_5b23d04fefa99' => '',
'field_5b23d04fefe85' => '',
'field_5b23d04ff0077' => '',
'field_5b23d04ff026c' => '',
'field_5b23d0bdb3c1a' => 'Version 1',
'field_5b23d0f48538b' => '',
'field_5b23d0f485772' => '',
'field_5b23d0d52be2d' => '',
'field_5b5ed10a6a7bc' => '',
'field_5b5ed10a6bcf5' => array(
array(
'field_5b5ed10acd264' => array(
array(
'field_5b5ed10b0c9ca' => '0',
'field_5b5ed10b0cce2' => 'TEST1234',
'field_5b5ed10b0d0fd' => 'Download title',
'field_5b5ed10b0d4e2' => 'EN',
'field_5b5ed10b0d72e' => 'A00',
'field_5b5ed10b0df27' => '887',
),
),
),
),
'field_5b23d088500a4' => '',
);

// recursively re-key array
function dostuff($input){
// always refer to self, even if you rename the function
$thisfunction = __function__;
$output = array();
foreach($input as $key => $value){
// change key
$newkey = (is_string($key) ? preg_replace('/^field_/', 'post_title_', $key) : $key);
// iterate on arrays
if(is_array($value)){
$value = $thisfunction($value);
}
$output[$newkey] = $value;
}
return $output;
}

var_dump(dostuff($input));

So I was looking at this and to my knowledge there is no wrapper function for recursion with callbacks, so here it is:

// general function for recursively doing something
// $input -> array() / the array you wan to process
// $valuefunction -> callable | null / function to run on all values *
// $keyfunction -> callable | null / function to run on all keys *
// * at least one has to defined or there is nothing to do
// callable has two inputs
// $input -> current branch
// $depth -> (int) how deep in the structure are we
// i.e: recursion($some_array, function($branch, $depth){something..}, 'trim');
function recursion($input, $valuefunction = false, $keyfunction = false){
if(!is_array($input)){
trigger_error('Input is '.gettype($input).'. Array expected', E_USER_ERROR);
return null;
}
if(!is_callable($valuefunction)){$valuefunction = false;}
if(!is_callable($keyfunction)){$keyfunction = false;}
if(!$valuefunction && !$keyfunction){
trigger_error('Input is unchanged!', E_USER_WARNING);
return $input;
}
// use recursion internally, so I can pass stuff by reference
// and do the above checks only once.
$recurse = function(&$branch, $depth = 0) use (&$recurse, &$valuefunction, &$keyfunction){
$output = array();
foreach($branch as $key => $value){
$key = $keyfunction ? $keyfunction($key, $depth) : $key;
$output[$key] = (is_array($value) ?
$recurse($value, $depth + 1) :
($valuefunction ?
$valuefunction($value, $depth) :
$value
)
);
}
return $output;
};
return $recurse($input);
}

$valuefunction = function($value, $depth){
return is_string($value) ? $depth.'_'.$value : $value;
};
function keyfunction($key){
return is_string($key) ? preg_replace('/^field_/', 'post_title_', $key) : $key;
}

var_dump(recursion($input, $valuefunction, 'keyfunction'));

Or for your example:

var_dump(recursion($input, 0, function($key){
return is_string($key) ? preg_replace('/^field_/', 'post_title_', $key) : $key;
}));

Iterating over, and mutating a multi-dimensional array of objects, of undetermined depth, with JS/Lodash

You almost got it. It's as simple as this:

static convertUrlsToSubTitles(collection) {
for (let item of collection) {
item.subtitle = item.url;
if (item.children) {
SortableMenu.convertUrlsToSubTitles(item.children);
}
}
}
  • Note that I omitted the return statement since technically it's not
    required - the function modifies the same "tree" object you pass into
    it as you call it because objects (such as arrays) are passed by

    reference and not copied and thus no need to return the same

    reference.

  • I used a for...of loop instead of Lodash's _.each or standard forEach since this avoids the need to create many function objects when traversing very deep "trees".

PHP editing multi dimensional array with unknown depth

You are near to achieve your goal only you have to do changes is to pass reference to foreach loop also. And here there is non requirement of key so I remove it. try this:

    $arr =Array
(
'level1' => Array
(
'level2_a' => Array
(
'a' => 786578,
'b' => 34450,
),

'level2_b' => Array
(
'a' => 786578,
'b' => 34450,
)

)

);

function calculateAverages(&$arr) {
if (is_array($arr) ) {

if (in_array('a',array_keys($arr))) {
$avg = ((array_sum($arr))/2);
$arr['c']=$avg;

}
else {
foreach($arr as &$data) {
calculateAverages($data);
}
}
}
}
calculateAverages($arr);
print_r($arr);

Example: https://eval.in/737280

Is there a way to loop until a multidimensional array is full in VBA?

Is this "simple"?

For i = 0 To 100

For j = 0 To 100

'Operations
MultiArray(i, j) = 1

Next j

Next i

Edit

Function IsArrayFilled(ByRef Multi_Array() As String) As Boolean

Dim i As Long, j As Long

For i = LBound(Multi_Array, 1) To UBound(Multi_Array, 1)

For j = LBound(Multi_Array, 2) To UBound(Multi_Array, 2)

If Multi_Array(i, j) = "" Then ' If there are ANY blank strings, then terminate the function immediately and return false

IsArrayFilled = False
Exit Function

End If

Next j

Next i

' If we got through the whole array without finding any blank strings, then return true
IsArrayFilled = True

End Function

Sub Test()

Dim MultiArray(0 To 100, 0 To 100) As String

Do While Not IsArrayFilled(MultiArray)

'Operations

Loop

End Sub

Note that this is a very expensive function, as it will attempt to iterate through the entire array every time it checks, i.e. it gets progressively slower as the array is filled. I'm unsure why you need to check for empty strings and why you can't simply iterate through the array once. However, this is the only way that I know to do what you're asking.

PHP: Iterating Through a multidimensional Array (4D)

By using a series of nested foreach with key => value iterators you can get the output you want; the key is not to output the date parts until you get to the bottom of the loops:

foreach ($post_search as $year => $months) {
foreach ($months as $month => $days) {
foreach ($days as $day => $files) {
foreach ($files as $file) {
echo "File $file:\nYear: $year\nMonth: $month\nDay: $day\n";
}
}
}
}

Output (for your sample data):

File default.md:
Year: 2019
Month: 5
Day: 12
File default.md:
Year: 2019
Month: 12
Day: 22
File default.md:
Year: 2020
Month: 5
Day: 19

Demo on 3v4l.org



Related Topics



Leave a reply



Submit