Setting new values in array based on query results I have some code which uses nested foreach loops to take the results of a query and drill down into the data, reassigning into a main data array at each level of the foreach nesting. The problem is that required reference variables are passed into the loops deeper down the tree, making it difficult to refactor.

I'd like to refactor this into a more functional style. I've looked into array_walk() and array_map() to try and accomplish this but they seem to be more suited to amending the array argument rather than reassigning to an external array.

I'm looking more for general approaches to refactoring out foreach loops in PHP with this sort of context rather than a direct solution to my problem.

foreach($grid_fields as $gridFieldKey =&gt; $gridFieldValue) {
    $grid_field_id = $gridFieldValue['field_id'];
    $grid_field_str = 'field_id_' . $grid_field_id;
    $grid_entry = $this-&gt;fetchGridEntry($grid_field_id, $data['entry_id']);

    if(sizeOf($grid_entry) &gt; 0) {
        $data[$grid_field_str] = array();
        $data[$grid_field_str]['entry_id'] = $cloned;

        foreach($grid_entry as $gridEntryKey =&gt; $gridEntryValue) {
            $grid_row = 'new_row_' . $gridEntryKey;
            $data[$grid_field_str]['rows'][$grid_row] = array();

            foreach($gridEntryValue as $geK =&gt; $geV) {
                if(strpos($geK, 'col_id_') !== false || strpos($geK, 'row_id') !== false) {
                    $data[$grid_field_str]['rows'][$grid_row][$geK] = $geV;
                }
            }
        }
    }
}
array_filter  removes elements from an array 
array_map changes
each element of an array to something else 
array_walk steps
through each element of an array (usually to create side effects)
(does not return anything)
array_reduce create something new from
an existing array


Every traditional loop (that does one thing) is one of the above.



&lt;?php
// Inside your object.
$data = array_reduce($grid_fields, add_grid_fields_to_data($cloned, $this), $data);

// Outside your object.
function add_grid_fields_to_data($cloned, $object) : array {
    return (function (array $data, array $grid_field) use ($cloned) {

        $grid_entry = $this-&gt;fetchGridEntry($grid_field['field_id'], $data['entry_id']);
        $grid_field_str = 'field_id_' . $grid_field_id;

        $data[$grid_field_str] = array();
        $data[$grid_field_str]['entry_id'] = $cloned;

        $no_grid_entry = sizeOf($grid_entry) == 0;
        $no_grid_entry ?: $data = array_reduce(array_keys($grid_entry), add_grid_entry_values_to_data($grid_entry, $grid_field_str), $data);

        return $data;
    })-&gt;bindTo($object);
}

function add_grid_entry_values_to_data(array $grid_entry, $grid_field_str) : Closure {
  return function (array $data, string $gridEntryKey) use ($grid_entry, $grid_field_str) : array {
    $gridEntryValue = $grid_entry[$gridEntryKey];
    $grid_row = 'new_row_' . $gridEntryKey;

    $data[$grid_field_str]['rows'][$grid_row] = array();

    $data = array_reduce(array_keys($gridEntryValue), add_grid_row_to_data($grid_row, $gridEntryValue), $data);
    return $data;  
  };
}

function add_grid_row_to_data(string $grid_row, array $gridEntryValue) : Closure {
    return function (array $data, $geK) use ($grid_row, $gridEntryValue) : data {
        $geV = $gridEntryvalue[$geK];

        $row_col_id_exists = strpos($geK, 'col_id_') !== FALSE || strpos($geK, 'row_id') !== FALSE;
        $row_col_id_exists ? $data[$grid_field_str]['rows'][$grid_row][$geK] = $geV : NULL;

        return $data;
    }; 
}