Why PHP Iteration by Reference Returns a Duplicate Last Record

Why php iteration by reference returns a duplicate last record?

I'll guess that you're reusing &$item here and that you're stumbling across a behavior which has been reported as bug a thousand times but is the correct behavior of references, which is why the manual advises:

Reference of a $value and the last array element remain even after the foreach loop. It is recommended to destroy it by unset().

foreach($arrayOfJsonMods as &$item)
{
//TODO memcached votes
}
unset($item);

See https://bugs.php.net/bug.php?id=29992

instances created in foreach loop are duplicates

You are using global variables instead of members. Remove

global $order_num, $name, $price, $cprice, $cheapest, $category;

From the function and preface each assignment with $this->

$this->order_num = (int)$arr[0];
$this->name = $arr[1];
$this->price = (float)$arr[2];
$this->cprice = (float)$arr[3];
$this->cheapest = $this->price <= $this->cprice;
$this->category = $arr[5];

PHP foreach duplicating last iteration of output data

The foreach with reference was my 1st thought, but since your original code didn't show it I didn't mentioned it.

For future reference, in PHP's foreach function documentation you can read:

Warning

Reference of a $value and the last array element remain even after the foreach loop. It is recommended to destroy it by unset().

Here's an example on how to workaround the problem:

<?php
$arr = array(1, 2, 3, 4);
foreach ($arr as &$value) {
$value = $value * 2;
}
// $arr is now array(2, 4, 6, 8)
unset($value); // break the reference with the last element

PHP Foreach Pass by Reference: Last Element Duplicating? (Bug?)

After the first foreach loop, $item is still a reference to some value which is also being used by $arr[2]. So each foreach call in the second loop, which does not call by reference, replaces that value, and thus $arr[2], with the new value.

So loop 1, the value and $arr[2] become $arr[0], which is 'foo'.

Loop 2, the value and $arr[2] become $arr[1], which is 'bar'.

Loop 3, the value and $arr[2] become $arr[2], which is 'bar' (because of loop 2).

The value 'baz' is actually lost at the first call of the second foreach loop.

Debugging the Output

For each iteration of the loop, we'll echo the value of $item as well as recursively print the array $arr.

When the first loop is run through, we see this output:

foo
Array ( [0] => foo [1] => bar [2] => baz )

bar
Array ( [0] => foo [1] => bar [2] => baz )

baz
Array ( [0] => foo [1] => bar [2] => baz )

At the end of the loop, $item is still pointing to the same place as $arr[2].

When the second loop is run through, we see this output:

foo
Array ( [0] => foo [1] => bar [2] => foo )

bar
Array ( [0] => foo [1] => bar [2] => bar )

bar
Array ( [0] => foo [1] => bar [2] => bar )

You'll notice how each time array put a new value into $item, it also updated $arr[3] with that same value, since they are both still pointing to the same location. When the loop gets to the third value of the array, it will contain the value bar because it was just set by the previous iteration of that loop.

Is it a bug?

No. This is the behavior of a referenced item, and not a bug. It would be similar to running something like:

for ($i = 0; $i < count($arr); $i++) { $item = $arr[$i]; }

A foreach loop isn't special in nature in which it can ignore referenced items. It's simply setting that variable to the new value each time like you would outside of a loop.

foreach inside while loop returning duplicate data

I'm not really sure what you're trying to do, but you are reassigning

$key = array() ;

immediately after your

foreach ($amount as $key) 

That's causing your

<td><?php echo $key;?></td>

to try to echo an array because you overwrote the value of $key assigned by the foreach.

Your post does not detail what data is getting duplicated so I can't really address that in this answer.

You are duplicating the same three rows because you are setting

$new = array_combine($item, $amount);

Then your SQL is grabbing the rows

$stmt = $dbh->query('SELECT * 
FROM `products`
WHERE `id` IN (' . $each . ')');

Then you're looping over the same items with

foreach ($new as $key => $val) {

If you want to display the items you found in the SQL then you shouldn't have the

foreach ($new as $key => $val) {

inside your while() loop. Your while() is already looping over the rows returned for those items. This assumes you only have one product per item number.

If you expect one or more 'products' to be returned for each item number then you should be executing your SQL while looping through foreach($new), but that doesn't appear to be what the top part of your code is doing.

After some back and forth we've identified the issue: the amounts need to be tied to the item numbers.

You are getting items numbers and quantities as arrays from your HTML. So you need to loop through the items and associate them with your quantities.

// where your qualities will live
$quantities = array() ;

// loop through the array of items you received and match them up with their quantity
foreach($item as $k=>$id) {
$quantities[$id] = $amount[$k] ;
}

Then you can access the quantity in your while loop using:

$row['quantity'] = $quantities[$row['id']] ;

PHP ForEach Loop inside while Returning Duplicate Rows

$namearray[]=$name;

This appends to the existing array. Do unset($namearray) before this statement and it should remove your duplicates I feel.

Nested foreach in php showing only last record..!

The issue is solved..
I needed to put
$data['products_brand'] = array();

above first foreach statment

PHP: array_push in loop results only the last record repeat

What your code is doing right now:

Every loop, it is changing the properties of one object, and appending the object's reference (pass by value, but the value is the reference.) to the $data array, so you have 4 spots in the $data array pointing to only one object. Each time you loop, you change the value, the last time it is assigned is on the last value. This is why all your values are the same.

What you want to do:

Create a new object every iteration with the data and point to the new object each time.

Other solution:

If you just want an array containing the values and not a reference to an object(like your output), you could try this:

$data = array();
for ($i = 0; $i < count($results); $i++) {
$row = $results[$i];
$data[$i]['objectId'] = $row['objectId'];
$data[$i]['username'] = $row['username'];
}
var_dump(json_encode($data));

PHP array last entries of all duplicate records

//My implementation is something like this.

set_time_limit (1500) ;
ini_set("memory_limit","128M");

$fileName = "_one";

$objScan = new scanCSV();

$objScan->setCSVFileName($fileName);
$objScan->loadCsvFile();
$objScan->separateDuplicateFromUniq();

$objScan->process();

class scanCSV
{
private $_csvFile = NULL;
private $_arrayListAll = NULL;
private $_allDuplicateRec = NULL;
private $_uniqueRec = NULL;

function setCSVFileName($fileName){
$this->_csvFile = $fileName;
}

//-----------------------------------------------------------------------
function loadCsvFile()
{
$arrayListAll = array();
if (($handle = fopen($this->_csvFile . ".csv", "r")) !== FALSE) {
while (($data = fgetcsv($handle, 1000, ",")) !== FALSE) {
$arrayListAll[] = $data;
}
}

$this->_arrayListAll = $arrayListAll;
}
//-----------------------------------------------------------------------
public function separateDuplicateFromUniq(){

$allDuplicateRec = array();
$uniqueRec = array();

foreach($this->_arrayListAll as $data){
if ($this->getcount($this->_arrayListAll, $data) > 1)
$allDuplicateRec[] = $data;
else
$uniqueRec[] = $data;
}

$this->_allDuplicateRec = $allDuplicateRec;
$this->_uniqueRec = $uniqueRec;

}
//-----------------------------------------------------------------------
public function process (){
$uniq = $this->removeDuplicate ($this->_allDuplicateRec);
$this->writeCSVFile ($this->_csvFile . "A.csv", $uniq);

$restofEntries = $this->removeLastEntries ($this->_arrayListAll, $uniq);
$this->writeCSVFile ($this->_csvFile . "B.csv", $restofEntries);
}
//-----------------------------------------------------------------------
function removeDuplicate ($allDuplicateRec)
{

foreach ($allDuplicateRec as $k => $v)
if ( $this->getcount($allDuplicateRec, $v) > 1 )
unset($allDuplicateRec[$k]);
return $allDuplicateRec;
}

//-----------------------------------------------------------------------
function removeLastEntries ($arrayListAll, $uniq){
foreach ($uniq as $entry)
if(in_array($entry, $arrayListAll))
unset($arrayListAll[array_search($entry, $arrayListAll)]);

return $arrayListAll;

}
//-----------------------------------------------------------------------
function getcount($arrayList1, $data){
$address = $data[2];
$count =0;
foreach ($arrayList1 as $dt)
if ($address == $dt[2])
$count++;

return $count;
}
//-----------------------------------------------------------------------
function writeCSVFile ($fileName, $data){

$fp = fopen($fileName, 'w');

foreach ($data as $k=>$fields)
fputcsv($fp, $fields);

fclose($fp);
}
//-----------------------------------------------------------------------
} // end of scan Optimized


Related Topics



Leave a reply



Submit