0

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;
    }
Goose
  • 4,764
  • 5
  • 45
  • 84

4 Answers4

2

$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)
}
Community
  • 1
  • 1
Christophe Weis
  • 2,518
  • 4
  • 28
  • 32
  • Giving me Invalid argument supplied for foreach(). Should I also be phrasing the weights array differently, or referencing $value as &$value inside the foreach? – Goose Jan 03 '15 at 13:49
  • I can not test with your object, but this worked for me as expected: ´$weights = array(1, 2, 3); foreach ($weights as &$value) { $value = $value * $kg_conv; }´ – Christophe Weis Jan 03 '15 at 13:58
  • My weights array is more complex, and I think that's why I'm getting different results – Goose Jan 03 '15 at 13:59
  • @Goose I added a testcase where I also compose an array in a similar way you did. For me that runs fine. What php version do you use? – Christophe Weis Jan 03 '15 at 14:11
  • @Goose Ok, what does var_dump($weights); displays before entering the foreach loop? For me it is an array of int as you can see in my edited post. – Christophe Weis Jan 03 '15 at 14:33
  • Ah, I think I might know the issue. It's changing $weights['data']['progress']->key but not $data['progress']->key. I'll check back soon. – Goose Jan 03 '15 at 14:39
  • It's working, I'll give you the check, but I think it would help future users if you updated your question to reflect that the original variable is not being changed, it's the array I put it in that's being changed. Thank you. – Goose Jan 03 '15 at 14:46
  • @Goose maybe you check my method? because it should work correctly and clearly. – maxpovver Jan 03 '15 at 14:57
  • @Goose It was already in my testcase code, the line which is commented out. But I will change my post to make it clearer. – Christophe Weis Jan 03 '15 at 15:08
2

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;
}
maxpovver
  • 1,580
  • 14
  • 25
0

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

sashko
  • 1,632
  • 1
  • 13
  • 20
0

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.

Community
  • 1
  • 1
Robert Rossmann
  • 11,931
  • 4
  • 42
  • 73
  • I tried both and no success. I think what I'm missing is what is meant by $key. Is $key suppose to be a foo type placeholder for an actual key? – Goose Jan 03 '15 at 14:03
  • `$key` is the index of the current element being iterated on; if you have array as mentioned above, `$key` will equal 0, 1, and 2, respectively for each iteration. If you would have array such as `$arr = ['first' => 'value', 'second' => 'value2']` then `$key` would equal *first*, *second*, respectively. See [PHP arrays](http://php.net/manual/en/language.types.array.php). – Robert Rossmann Jan 03 '15 at 14:12
  • I am only trying to iterate some of the elements. Will that be a problem here. I'll dig into this, and let you know my results. – Goose Jan 03 '15 at 14:29