Goose
Goose

Reputation: 4821

php foreach with arrays and objects and redefining their value

I'm trying to foreach through an array of objects inside an array. I'm not receiving errors, but it seems not to be storing the new value I'm trying to establish. I've found many answers on here closely related to this question, but I'm not understanding, and I'm hoping someone can make this clearer.

Essentially, this converts kg to lbs.

    $kg_conv = 2.20462262;
    $weights = array($data['progress']->weight, $data['progress']->squats,
    $data['progress']->bench, $data['progress']->deadlift); 

    foreach ($weights as $value) {
        $value = $value * $kg_conv;
    }

The values in the array come out unchanged. From what I've read, I should be using $data['progress'] and iterating through that, but I'm not understanding how to refer to only some of the elements inside instead of all of them. I also tried cutting out some redundancy by storing the objects in $progress instead of $data['progress'] but still not success.

UPDATE: The following has not worked

1

    foreach ($weights as &$value) {
        $value = $value * $kg_conv;
    }

2

    foreach ($weights as $key => &$value) {
        $value = $value * $kg_conv;
    }

3

    foreach ($weights as $key => $value) {
        $weights[$key] = $value * $kg_conv;
    }

Upvotes: 0

Views: 887

Answers (4)

CHW
CHW

Reputation: 2664

$value contains only a copy of the array element.

You can pass the object by reference like this:

foreach ($weights as &$value) {
    $value = $value * $kg_conv;
}

Note the & before $value.

See References Explained in the php documentation or What's the difference between passing by reference vs. passing by value? here on stackoverflow.

Edit 2:

But note that this will only change the values in the $weights array, not your object stored in $data['progress'], as it's value has been copied into $weights. To achieve this, you can reference the object properties when constructing the data array in the following way (this step was commented out in my testcase code) :

$weights = array(&$data['progress']->weight, &$data['progress']->squats,
    &$data['progress']->bench, &$data['progress']->deadlift);

Note the & operator again.

But probably the solution of maxpower is cleaner for your requirement.

Edit:

After your comment I created the following test case which works fine:

<?php
class foo {
    public $test = 5;
    public $test2 = 10;
}

$obj = new foo();
$obj2 = new foo();
$obj2->test = 3; 

$data =  array('progress' => $obj, 'p2' => $obj2);

// this copies the values into the new array
$weights = array($data['progress']->test, $data['progress']->test2);

// this makes a reference to the values, the original object will be modified
// $weights = array(&$data['progress']->test, &$data['progress']->test2);

var_dump($weights);

$kg_conv = 2.20462262;

foreach ($weights as &$value) {
    $value = $value * $kg_conv;
}
var_dump($weights);

The result is:

array(2) {
  [0]=>
  int(5)
  [1]=>
  int(10)
}
array(2) {
  [0]=>
  float(11.0231131)
  [1]=>
  &float(22.0462262)
}

Upvotes: 2

maxpovver
maxpovver

Reputation: 1600

it does not work because you do not modify data array, you only modify copy made from this array, which is another array in memory

Solution: (will work for object's fields too)

$kg_conv = 2.20462262;
$weights = ['weight', 'squats','bench', 'deadlift']; 

foreach ($data['progress'] as $key=>&$value) {
  if ( in_array($key,$weights)
    $value = $value * $kg_conv;
}

Upvotes: 2

sashko
sashko

Reputation: 1682

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. In your example code should look like this:

foreach ($weights as &$value) {
    $value = $value * $kg_conv;
}

But referencing $value is only possible if the iterated array can be referenced (i.e. if it is a variable). You can also use indexes to acces array value directly. This code works pretty fine too:

foreach ($weights as $key => $value) {
    $weights[$key] = $value * $kg_conv;
}

More here: http://php.net/manual/en/control-structures.foreach.php

Upvotes: 0

Robert Rossmann
Robert Rossmann

Reputation: 12131

The PHP foreach manual describes this topic in great detail. To summarise why you are not seeing the results you expect, here is a short explanation.

Imagine you have an array $arr = ['one', 'two', 'three']. When you execute a foreach loop on this array, PHP will internally create a copy of your array:

foreach ($arr as $key => $value) {
    $value = 'something'; // You are modifying the copy, not the original!
}

To modify the original array, you have two options.

Use the $key variable to access the element at given key in the original array:

$arr[$key] = 'something'; // Will change the original array

Tell PHP to use a reference to the original element in $value:

foreach ($arr as $key => &$value) {
    $value = 'something'; // $value is reference, it WILL change items in $arr
}

Both approaches are valid; however, the second is more memory-efficient because PHP does not have to copy your array when you loop through it - it simply references the original.

Upvotes: 0

Related Questions