PHP - array unset not working in foreach loop - php

I have various items in my $positions array and do the following:
foreach ($positions as &$position) {
if ($position['date'] == $order['date']) {
unset($position);
}
}
var_dump($positions);
The var_dump still displays the $position which should be excluded.
When I do
foreach ($positions as $key => &$position) {
if ($position['date'] == $order['date']) {
unset($positions[$key]);
}
}
It does remove far more items than expected.
Anybody knows what happens here in both cases and how why does unset($position) not work? I reference the item in the foreach loop with '&'.
Thanks!

Instead of using &$variableName use $varibaleName, because there is not concept of pointers in php and not proper used of reference operator causes unexpected results sometimes.
foreach ($positions as $key => $eachPosition)
{
if ($eachPosition['date'] == $order['date'])
{
unset(positions[$key]);
}
}

foreach ($positions as $position) {
($position['date'] == $order['date']) ? unset($position['date']) : '';
}
I hope help you. :)

Assuming you want to delete the $position['date'] value; instead of using unset($positions[$key]) you could do the following :
foreach ($positions as $key => $position) {
if ($position['date'] == $order['date']) {
unset($position['date']);
}
}
NB : I removed the reference in the foreach loop because according to your example it's not used and can cause unexpected behaviours (PHP foreach by reference causes weird glitch when going through array of objects).

Related

Finding a string by looping through an array php

I have this array $filelist
Array ( [0] => . [1] => .. [2] => .DS_Store [3] => 11-96.eml [4] => 11-97.eml )
Which is a list of all files in a particular directory... Sometimes there is no .DS_Store file, sometimes there is, sometimes there are 2 eml files, sometimes there are as many as 6. I'm trying to loop through and find the first array position a .eml file exists so that I can work out how many file are eml and where to start referencing the array.. I have tried...
function firstFileInList($filelist) {
$x = 0;
foreach ($filelist as $value) {
if(strpos($filelist, ".eml") == false) {
$x = $x + 1;
//break;
}
}
return $x;
}
This returns 5, if I include the break it returns 1 and I was expecting to get 4.
Please could somebody point me in the right direction or even if there is a completely better way to do this then I would be more than grateful to see that...
Thanks Paul,
break exists every PHP loop directly when it is called, even if there are other elements. Use continue to get to the next element.
Based on your question
I'm trying to loop through and find the first array position a .eml file exists
function firstFileInList($filelist) {
foreach($filelist as $k => $v)
if(strpos($v, ".eml") !== false)
return $k;
return false;
}
The best way to grab a key from an array in a foreach is to define the key before it starts. Try updating your code with this:
function firstFileInList($filelist) {
$x = false;
foreach ($filelist as $key => $value) {
if(strpos($value, ".eml") == false) {
$x = $key;
break;
}
}
return $x;
}
What this does is set $x to the actual key instead of a number that you increment like you would in a for() loop. $key will always be the array key, so in this example 0, 1, 2, 3, 4.
function firstFileInList($filelist) {
$key=false;
foreach ($filelist as $key=>$value) {
if(strpos($value, ".eml") !==false){
break;
}
}
return $key;
}
In case there is no match you get false.
The problem lies here:
foreach ($filelist as $value) {
if(strpos($filelist, ".eml") == false) {
Note that, for the foreach loop as you have written, it takes each element of the $filelist array, and puts it into the $value variable. Maybe you don't have warnings turned on in PHP, but when I tried your code, I got the following:
Warning: strpos() expects parameter 1 to be string, array given in test/a.php on line 6
What you want is
foreach ($filelist as $value) {
if(strpos($value, ".eml") == false) {
$x = $x + 1;
}
}
Note $value in the second line.

Removing Keys In Associative Array Not Working

I am trying to retain only certain keys, and remove the rest from an external API. I have an array (http://pastebin.com/vU8T4y7h), "data" containing the objects:
foreach ($data as $media) {
foreach (array_keys($media) as $media_key) {
if ($media_key!=="created_time" && $media_key!=="likes" && $media_key!=="images" && $media_key!=="id") {
unset($media[$media_key]);
}
}
}
In this case, I am trying to only keep the created_time, likes, images, and id keys, however, the above code isn't working. Any ideas as to why? Any other elegant solutions to achieve the same thing?
The reason this isn't working is because you aren't unsetting from the original $data object. You can fix it one of two ways. Either access by reference or update your unset to act on the original $data object instead.
Using reference:
foreach($data as &$media) {
Unsetting from $data
unset($data[$media][$media_key]);
foreach creates a copy of each array element, so unsetting $media[$media_key] only unsets from the copy; and you want to unset from the original $data array:
foreach ($data as $mediaRef => $media) {
foreach (array_keys($media) as $media_key) {
if ($media_key!=="created_time" && $media_key!=="likes" && $media_key!=="images" && $media_key!=="id") {
unset($data[$mediaRef][$media_key]);
}
}
}
or (by reference)
foreach ($data as &$media) {
foreach (array_keys($media) as $media_key) {
if ($media_key!=="created_time" && $media_key!=="likes" && $media_key!=="images" && $media_key!=="id") {
unset($media[$media_key]);
}
}
}
unset($media);
Asking for an elegant solution:
foreach ($data as $key => $media) {
foreach (array_keys($media) as $media_key) {
if (!in_array($media_key, array("created_time", "likes", "images", "id"))) {
unset($data[$key][$media_key]);
}
}
}
I'd try to avoid using references in foreach loops because you need to unset the referenced variable. If not, hard to detect errors occur.
You can use array_intersect_key to do that kind of thing easily:
$keepTheseKeys = array_flip(array('created_time', 'likes', 'images', 'id'));
foreach ($data as &$media) {
$media = array_intersect_key($media, $keepTheseKeys);
}
unset($media);
The error
The error in the code in the question is that you're updating a temporary variable, either pass media by reference (see above example) - or refer to the original variable:
foreach ($data as $i => $media) {
$data[$i] = ...
First, you don't need to use
foreach (array_keys($media) as $media_key) {
you can use
foreach($media as $media_key => $media_value){
instead
But for your question, in the first line, you are going through $data, and in each cycle, the current value in $data is assigned into $media. But media doesn't have any connection back to original $data, so it is discarded on next iteration, when new value is assigned from $data to $media.
You have two options:
Use references:
foreach($data as &$media) { // and so on... this should work, but i don't suggest it
in first line, write
foreach($data as $media_k => $media) {
and in fourth line, write
unset($data[$media_k][$media_key]); //

PHP Array structure

I am trying to generate an MS Excel spread sheet using PHPExcel 1.7.6 . I am having trouble determining the structure of the array expected.
The code that builds up the columns and rows is as follows:
function _headers() {
$i=0;
foreach ($this->data[0] as $field => $value) {
if (!in_array($field,$this->blacklist)) {
$columnName = Inflector::humanize($field);
$this->sheet->setCellValueByColumnAndRow($i++, 4, $columnName);
}
}
$this->sheet->getStyle('A4')->getFont()->setBold(true);
$this->sheet->getStyle('A4')->getFill()->setFillType(PHPExcel_Style_Fill::FILL_SOLID);
$this->sheet->getStyle('A4')->getFill()->getStartColor()->setRGB('969696');
$this->sheet->duplicateStyle( $this->sheet->getStyle('A4'), 'B4:'.$this->sheet->getHighestColumn().'4');
for ($j=1; $j<$i; $j++) {
$this->sheet->getColumnDimension(PHPExcel_Cell::stringFromColumnIndex($j))->setAutoSize(true);
}
}
function _rows() {
$i=5;
foreach ($this->data as $row) {
$j=0;
foreach ($row as $field => $value) {
if(!in_array($field,$this->blacklist)) {
$this->sheet->setCellValueByColumnAndRow($j++,$i, $value);
}
}
$i++;
}
}
I'm currently getting an 'Invalid argument supplied for foreach()' error.
I would appreciate it if somebody can outline the correct array structure required.
As IsisCode said, it sounds like you're looking in the wrong direction by associating the problem with PHPExcel. That error is generally just saying that the first argument supplied to foreach() isn't an array.
I don't see anything indicating that the problem is explicitly with the initial foreach in the _headers() method though. If there's a non array element in $this->data then your _rows() method could produce the error as well.
Given:
$this->data = Array(
Array('foo' => 'bar'),
'baz'
)
That would cause the second foreach in _rows() to fail, as an example. Your error message should be able to point you to which foreach() is failing, but ultimately it sounds like you've got a non-array element in $this->data where you don't expect it. If that can't be helped, then consider verifying you're dealing with an array before calling foreach:
function _rows() {
$i=5;
foreach ($this->data as $row) {
$j=0;
if(!is_array($row)) { continue; } // Ignore non-array elements
foreach ($row as $field => $value) {
if(!in_array($field,$this->blacklist)) {
$this->sheet->setCellValueByColumnAndRow($j++,$i, $value);
}
}
$i++;
}
Verifying the type of a variable before handing it to a type-specific function is never a bad idea and can save a lot of headache in general.

PHP arrays - a 'set where key=?' type function?

Is there a built in php function that allows me to set a value of an array based on a matching key? Maybe i've been writing too much SQL lately, but i wish I could perform the following logic without writing out nested foreach array like the following:
foreach($array1 AS $k1 => $a1) {
foreach($array2 AS $a2) {
if($a1['id'] == $a2['id']) {
$array[$k1]['new_key'] = $a2['value'];
}
}
}
Is there a better way to do this? In SQL logic, it would be "SET array1.new_key = x WHERE array1.id = array2.id". Again, i've been writing too much SQL lately :S
When I need to do this, I use a function to first map the values of one array by id:
function convertArrayToMap(&$list, $attribute='id') {
$result = array();
foreach ($list as &$item) {
if (is_array($item) && array_key_exists($attribute, $item)) {
$result[$item[$attribute]] = &$item;
}
}
return $result;
}
$map = convertArrayToMap($array1);
Then iterate through the other array and assign the values:
foreach ($array2 AS $a2) {
$id = $a2['id'];
$map[$id]['new_key'] = $a2['value'];
}
This are less loops overall even for one pass, and it's convenient for further operations in the future.
This one is fine and correct
foreach(&$array1 AS &$a1) {
foreach($array2 AS $a2) {
if($a1['id'] == $a2['id']) {
$a1['new_key'] = $a2['value'];
}
}
}

How to go to next record in foreach loop

In the following code, if $getd[0] is empty, I want to go to the next record.
foreach ($arr as $a1) {
$getd = explode(',' ,$a1);
$b1 = $getd[0];
}
How can I achieve it?
We can use an if statement to only cause something to happen if $getd[0] is not empty.
foreach ($arr as $a1) {
$getd=explode(",",$a1);
if (!empty($getd[0])) {
$b1=$getd[0];
}
}
Alternatively, we can use the continue keyword to skip to the next iteration if $getd[0] is empty.
foreach ($arr as $a1) {
$getd=explode(",",$a1);
if (empty($getd[0])) {
continue;
}
$b1=$getd[0];
}
Using continue which will skip to the next iteration of the loop.
foreach ($arr as $a1){
$getd=explode(",",$a1);
if(empty($getd[0])){
continue;
}
$b1=$getd[0];
}

Categories