142

I was wondering if it is possible to edit the current object that's being handled within a foreach loop

I'm working with an array of objects $questions and I want to go through and look for the answers associated with that question object in my db. So for each question go fetch the answer objects and update the current $question inside my foreach loop so I can output/process elsewhere.

foreach($questions as $question){
    $question['answers'] = $answers_model->get_answers_by_question_id($question['question_id']);
}
mickmackusa
  • 43,625
  • 12
  • 83
  • 136
Garbit
  • 5,805
  • 6
  • 39
  • 72
  • As both ArtjomKurapov and @topener suggested I was looking for 'pass by reference' using the & sign. Thanks chaps :) have a nice day – Garbit Apr 12 '12 at 10:12

2 Answers2

265

There are 2 ways of doing this

foreach($questions as $key => $question){
    $questions[$key]['answers'] = $answers_model->get_answers_by_question_id($question['question_id']);
}

This way you save the key, so you can update it again in the main $questions variable

or

foreach($questions as &$question){

Adding the & will keep the $questions updated. But I would say the first one is recommended even though this is shorter (see comment by Paystey)

Per the PHP foreach documentation:

In order to be able to directly modify array elements within the loop precede $value with &. In that case the value will be assigned by reference.

lloiacono
  • 4,714
  • 2
  • 30
  • 46
Rene Pot
  • 24,681
  • 7
  • 68
  • 92
  • Nice! so the & sign will pass by reference? is that the correct terminology? – Garbit Apr 12 '12 at 10:09
  • you are missing part with passing entire `$question` variable into get_answers_by_question_id() – Artjom Kurapov Apr 12 '12 at 10:10
  • @ArtjomKurapov what do you mean? What part – Rene Pot Apr 12 '12 at 10:11
  • 43
    References in `foreach` is really not recommended, the way the `foreach` passes around the value part of the loop results in unpredicatble behaviour. It may be longer but you're far safer using method 1 here. – Paystey Apr 12 '12 at 10:11
  • @Topener well he needs $question to be updated inside `get_answers_by_question_id`.. and you are just passing its id – Artjom Kurapov Apr 12 '12 at 10:12
  • @ArtjomKurapov I think either you or me misunderstood the question. Thats not what I see he asked – Rene Pot Apr 12 '12 at 10:12
  • @Topener right.. I thought `get_answers_by_question_id` does more than just fetch data from DB and actually changes `$question` inside too – Artjom Kurapov Apr 12 '12 at 10:15
  • I've found that both methods above work however considering what @Paystey has said I've used the Key Value pair type loop instead of the by reference, it seems messy having the use unset($question) after the loop – Garbit Apr 12 '12 at 12:37
  • 2
    I just spent a perplexed hour debugging an issue caused by using a reference in a foreach. I re-used the same variable name for a second foreach call - as I'd passed the first by reference, it kept modifying the last item in the array! Using an explicit index wouldn't have had this problem. – Hippyjim Jan 24 '14 at 10:03
  • 10
    @Paystey can you cite your sources or give a detailed explaination ? – Nico Oct 08 '15 at 14:22
  • 3
    Why would manipulating references be unsafe? Is C/C++ where you have to manipulate references everywhere unsafe? It's up to you to make it safe or not, not the language. – Kalzem Feb 14 '16 at 14:41
  • 5
    @BabyAzerty: Paystey didn't say references "in general", but in `foreach`, regarding horror like this: http://stackoverflow.com/questions/3307409/php-pass-by-reference-in-foreach (@Nico, FYI, too.) – Sz. Feb 19 '17 at 13:11
10

Surely using array_map and if using a container implementing ArrayAccess to derive objects is just a smarter, semantic way to go about this?

Array map semantics are similar across most languages and implementations that I've seen. It's designed to return a modified array based upon input array element (high level ignoring language compile/runtime type preference); a loop is meant to perform more logic.

For retrieving objects by ID / PK, depending upon if you are using SQL or not (it seems suggested), I'd use a filter to ensure I get an array of valid PK's, then implode with comma and place into an SQL IN() clause to return the result-set. It makes one call instead of several via SQL, optimising a bit of the call->wait cycle. Most importantly my code would read well to someone from any language with a degree of competence and we don't run into mutability problems.

<?php

$arr = [0,1,2,3,4];
$arr2 = array_map(function($value) { return is_int($value) ? $value*2 : $value; }, $arr);
var_dump($arr);
var_dump($arr2);

vs

<?php

$arr = [0,1,2,3,4];
foreach($arr as $i => $item) {
    $arr[$i] = is_int($item) ? $item * 2 : $item;
}
var_dump($arr);

If you know what you are doing will never have mutability problems (bearing in mind if you intend upon overwriting $arr you could always $arr = array_map and be explicit.

MrMesees
  • 1,488
  • 19
  • 27