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 standardforEach
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
Designing a Secure Auto Login Cookie System in PHP
How to Store Birthdate and Age So That Age Can Be Updated Daily in PHP/Mysql
How to Send a Bytearray (From Flash) and Some Form Data to PHP
How to Show Ajax Loading Gif Animation While the Page Is Loading
Send Checkbox Value in PHP Form
Utf8_(En|De)Code Removed from PHP7
Laravel Cannot Delete or Update a Parent Row: a Foreign Key Constraint Fails
Syntax Error, Unexpected T_Encapsed_And_Whitespace, Expecting T_String or T_Variable or T_Num_String
Laravel and View Caching in Development -- Can't See Changes Right Away
Php-Sort Array Based on Another Array
PHP Authentication with Multiple Domains and Subdomains
Google API Client "Refresh Token Must Be Passed in or Set as Part of Setaccesstoken"