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]); //
Related
I'm processing json file with php with little difference in the beginning of the file ,my problem now is that i built two foreach loops and i am looking for a way to make it one loop.
{"code":0,"rid":"0","data":{"subid":"9174906486819979969","data":{"more":
{"code":0,"rid":"0","data":{"9174906486819979969":{"more":
Now I'm doing like that and it double the code for processing , the rest of the json file is all the same.
foreach($json['data']['data'])
{
}
foreach($json['data'])
{
}
What i need is one foreach loop instead of 2, is that possible?
You can check for the key and if present use it, if not then get the first key:
if(isset($json['data']['data'])) {
$data = $json['data']['data'];
} else {
$data = reset($json['data']); //or current()
}
Or shorter:
$data = $json['data']['data'] ?? reset($json['data']);
Then:
foreach($data as $values) {
//code
}
Another option would be to loop and check for an array, depending on the structure you have:
foreach($json['data'] as $values) {
if(is_array($values)) {
foreach($values as $v) {
//code
}
}
I am trying to store values in a for loop as an array so I can access them outside them outside the loop.
foreach ($doctor->booking as $booking) {
$bookeddate = date('Y-m-d', strtotime($booking->booked_time));
if( $bookeddate == $end_date ) {
$booked_time[] = date('H:i a', strtotime($booking->booked_time));
}
}
foreach ($booked_time as $key ) {
echo $key;
}
This code keeps giving me an error "Undefined variable: booked_time"
try
initializing $booked_time before using as it has scope inside function only
$booked_time = [];
foreach ($doctor->booking as $booking) {
$bookeddate=date('Y-m-d',strtotime($booking->booked_time));
if($bookeddate==$end_date){
$booked_time[]=date('H:i a',strtotime($booking->booked_time));
}
}
foreach ($booked_time as $key ) {
echo $key;
}
This is not applicable...
The array you were created is valid only for that scope(here it is for your FOREACh).
As soon as you are get out from that scope,the array variable is diappeared.
SOLUTION -
Declare your array in a global scope where both two foreach can be accessed.
Three things you should do:
initialize the variable $booked_time as empty array before looping: then you are sure to always have an array defined before the loop, as in:
$booked_time = []; - this will make the error disappear
verify $end_date actually has a value (the definition is not included in your code snippet) so there is something to compare
if it does have a value, ensure $end_date is formatted as date("Y-m-d") just like the bookeddate because you are doing a string comparison, where date("Y-m-d") is not the same as date("Ymd") even though they reference the exact same day
This is not possible because of the concept of block scope. You can read more about variable scoping in PHP here.
Generally, this means, that every variable declared within curly braces is only accessible in this block of code.
The easiest way to work around this issue is to create an empty array beforehand, like so:
$booked_time = [];
foreach ($doctor->booking as $booking) {
$bookeddate=date('Y-m-d',strtotime($booking->booked_time));
if($bookeddate==$end_date){
$booked_time[]=date('H:i a',strtotime($booking->booked_time));
}
}
foreach ($booked_time as $key ) {
echo $key;
}
create array outside the for loop.
then use array_push() to add elements to the array
possible solution
$booked_times = [];
foreach ($doctor->booking as $booking) {
$bookeddate = date('Y-m-d',strtotime($booking->booked_time));
if($bookeddate==$end_date){
array_push($booked_times, date('H:i a',strtotime($booking->booked_time));
}
}
foreach ($booked_time as $key ) {
echo $key;
}
You may want to consider using a collection. They are really cool and you have access to so many methods. https://laravel.com/docs/5.7/collections.
I'm going to use code from the post you liked above and just change the array to a collection.
$booked_time = collect();
foreach ($doctor->booking as $booking) {
$bookeddate=date('Y-m-d',strtotime($booking->booked_time));
if($bookeddate==$end_date){
$booked_time->push(date('H:i a',strtotime($booking->booked_time)));
}
}
foreach ($booked_time as $key ) {
echo $key;
}
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).
I'm working with a JSON string. I'm converting it to an associative array to find specific values and change those values when a certain key is found (['content']). The depth of the array is always unknown and will always vary.
Here is the function I wrote. It takes an array as an argument and passes it by reference so that the variable itself is modified rather than a copy of it scoped locally to that function.
$json_array = json_decode($json_string, true);
function replace_data(&$json_array, $data='REPLACE TEST')
{
foreach($json_array as $key => $value) {
if ($key == 'content' && !is_array($value)) {
$json_array[$key] = $data;
} else {
if (is_array($value)) {
replace_data($value, $data);
}
}
}
}
replace_data($json_array, "test test test");
var_dump($json_array);
What I'm expecting to happen is every time a key ['content'] is found at no matter what depth, it replaces with that value specified in the $data argument.
But, when I var_dump($json_array) Those values are unchanged.
What am I missing?
With array_walk_recursive:
function replace_data($json_array, $data = 'REPLACE TEST') {
array_walk_recursive($json_array, function (&$value, $key) use ($data) {
if (!is_array($value) && $key === 'content') {
// $value passed by reference
$value = $data;
}
});
return $json_array;
}
And without references:
function replace_data($json_array, $data = 'REPLACE TEST') {
foreach ($json_array as $key => $value) {
if (is_array($value)) {
$json_array[$key] = replace_data($value, $data);
} elseif ($key === 'content') {
$json_array[$key] = $data;
}
}
return $json_array;
}
To expand on my comment, you need another reference here:
foreach($json_array as $key => &$value) {
That way, a reference to the original value is passed when you make the recursive call, rather than the local copy created with the foreach loop.
From the PHP manual entry for foreach:
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.
You'll also see in the manual that it recommends unsetting the reference to $value after the loop. Even though it probably won't cause any problems if you don't do that in your function, it's best to be in the habit of always unsetting references created in foreach loops like that. It can cause some strange looking problems if you don't.
From PHP7.4, "arrow function" syntax offers a clean and short approach.
As leafnodes are iterated, if the key is content, then replace the text, otherwise do not change the value.
There are no returns being used. All mutations are applied directly to the passed in variables prefixed with &.
function replace_data(&$array, $replaceWith = 'REPLACE TEST')
{
array_walk_recursive(
$array,
fn(&$v, $k) => $v = ($k === 'content' ? $replaceWith : $v)
);
}
replace_data($json_array, "test test test");
var_export($json_array);
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.