3

Some times I need to modify an array key for example something like:

$temp = array();
foreach($arrayIWantToModify as $key => $value) {
    if($value % 2) == 0) {
        $temp['odd_' . $key] = $value;
    } else {
        $temp[$key] = $value;
    }
}

I always use a temp array to achieve that, but I had a memory problem when working on a project that had some big arrays. Of course the best solution is to increase the memory but, but i was just wondering is it so bad to just do it like:

foreach($arrayIWantToModify as $key => $value) {
    if(($value % 2) == 0) {
        $arrayIWantToModify['odd_' . $key] = $value;
        unset($arrayIWantToModify[$key]);
    }
}

It seems to work but I always saw it as a bad practice because you are changing an array you are iterating.

3 Answers 3

5

I would agree that this is not a good idea. Even if the language you're using defines what should happen in this situation (I don't know if PHP does; that's a question for StackOverflow) most people who see code like this would still have to look up whether or not it's defined behavior before they could have any confidence in it.

If the list of desired modifications is typically much smaller than the array being modified, I would always prefer using a temporary array to contain the desired modifications until the iterating is complete, just like your first example. If you're running out of memory doing that, that tells me you're modifying such a huge portion of the array that you'd be better off with a map operation (I believe PHP calls it array_map) that's designed to efficiently modify all elements without any risk of processing an element more or less than one time.

3

In PHP the foreach loop creates a temporary variable storing the initial array passed. you can pass $value by reference using &$value. In the two examples below the loops remove the next element in the array.

Since it is a temporary variable changes to the original have no effect on the foreach loop.

<?php 
$arr = array(1,2,3,4,5,6,7,8,9); 

foreach($arr as $key=>$value) 
{ 
    unset($arr[$key + 1]); 
    echo $value . PHP_EOL; 
} 
?> 

Output: 1 2 3 4 5 6 7 8 9

<?php 
$arr = array(1,2,3,4,5,6,7,8,9); 

while (list($key, $value) = each($arr)) 
{ 
    unset($arr[$key + 1]); 
    echo $value . PHP_EOL; 
} 
?> 

Output: 1 3 5 7 9

0

Let's use a slightly different take on the problem. You describe what you do as:

Some times I need to modify an array key

When we look at it like this, we have the problem of doing everything in the same loop. Can we look at it differently? Maybe say:

Some times I need to find a set of keys, and then I need to modify those keys in the array

Now we can see two separate tasks to complete, which means we can break them apart. The code to accomplish this might look something like:

$oddKeys = array();

// Task 1: Find a set of keys
foreach($arrayIWantToModify as $key => $value) {
    if($value % 2 == 0) {
        $oddKeys[] = $key;
    }
}

// Task 2: Modify those keys
foreach($oddKeys as $key) {
    $arrayIWantToModify['odd_' . $key] = $arrayIWantToModify[$key];
    unset($arrayIWantToModify[$key]);
}

We have less memory usage* than the temporary array as we are only storing the keys that need to change, not an entire duplicate array. The code is also clearer than the alternative as there is no question about the behavior here. Since we've broken the behavior apart, we can now even extract it and reuse it elsewhere (for example by extracting a function that returns an array of keys where the values meet certain criteria).


* If the number of keys that need to change is the same or nearly the same as the total number of keys, the memory usage gains may not be as large. You would need to test whether you are still running into memory limitations, and possibly come up with a different strategy (say, swap all the keys, then reverse the action on the reverse condition).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.