Multidimensional arrays in PHP - how to add values using foreach - php

I have a multidimensional array defined as follows
$SquadList = array('name', 'position', 'dob', 'nation', 'games', 'goals', 'assists');
I'm looping through several foreach loops and storing data from JSON
foreach ($season as $key => $squad){
$SquadList[0] = $squad['full_name'];
$SquadList[1] = $squad['position'];
$SquadList[2] = gmdate("d-m-y", $birthday);
$SquadList[3] = $squad['nationality'];
$SquadList[4] = $squad['appearances_overall'];
$SquadList[5] = $squad['goals_overall'];
$SquadList[6] = $squad['assists_overall']; }
foreach ($season1 as $key => $squad){
$SquadList[0] = $squad['full_name'];
$SquadList[1] = $squad['position'];
$SquadList[2] = gmdate("d-m-y", $birthday);
$SquadList[3] = $squad['nationality'];
$SquadList[4] = $squad['appearances_overall'];
$SquadList[5] = $squad['goals_overall'];
$SquadList[6] = $squad['assists_overall'];
The code is messy. The output is only 2 elements when it should be 30+
I've tried array_push as follows
array_push($SquadList['name'], $squad['full_name'];

i'm not sure if i get the question correctly, but i imagine you want it to be structured something like this:
$SquadList = []; // define it as an array
$ctr = 0; // define a counter that would be used in the two iterations
foreach ($season as $key => $squad){
$SquadList[$ctr][0] = $squad['full_name'];
$SquadList[$ctr][1] = $squad['position'];
$SquadList[$ctr][2] = gmdate("d-m-y", $birthday);
$SquadList[$ctr][3] = $squad['nationality'];
$SquadList[$ctr][4] = $squad['appearances_overall'];
$SquadList[$ctr][5] = $squad['goals_overall'];
$SquadList[$ctr][6] = $squad['assists_overall'];
$ctr++; // increase counter
}
foreach ($season1 as $key => $squad){
$SquadList[$ctr][0] = $squad['full_name'];
$SquadList[$ctr][1] = $squad['position'];
$SquadList[$ctr][2] = gmdate("d-m-y", $birthday);
$SquadList[$ctr][3] = $squad['nationality'];
$SquadList[$ctr][4] = $squad['appearances_overall'];
$SquadList[$ctr][5] = $squad['goals_overall'];
$SquadList[$ctr][6] = $squad['assists_overall'];
$ctr++; // increase counter
}

The reason you had two results is because you got the last squad for each season. This happened because each time a season iterated, it overwrote the previous squad.
To solve this problem, $SquadList must be an array. But you have to assign all its members at once, otherwise the array will increment every time you add a member.
Populating an array of arrays
foreach ($season as $key => $squad) {
$squadList[] = [
$squad['full_name'],
$squad['position'],
gmdate("d-m-y", $squad['birthday']),
$squad['nationality'],
$squad['appearances_overall'],
$squad['goals_overall'],
$squad['assists_overall']
];
}
Note a couple of changes I made:
I removed the capitalization on $squadList because convention has starting with a capital indicating an object, not a plain old variable
$birthday was undefined, so I made an educated guess
Cleaning up the code
You mentioned that “the code is messy”. That is a very healthy observation to make.
What you are noticing is the result of two things:
Your code is repeating itself (a violation of DRY - Don’t Repeat Yourself)
Need to follow convention of PSR-12
So let’s get rid of the code duplication
Refactoring
When you start repeating yourself, that’s a signal to pull things into a function
function buildSquad(array $season)
{
foreach ($season as $key => $squad) {
$squadList[] = [
$squad['full_name'],
$squad['position'],
gmdate("d-m-y", $squad['birthday']),
$squad['nationality'],
$squad['appearances_overall'],
$squad['goals_overall'],
$squad['assists_overall']
];
}
return $squadList;
}
$squadList = [];
// if you just want to lump them all together
$squadList[] = buildSquad($season);
$squadList[] = buildSquad($season2);
// etc

Related

php array returning duplicate values

I am trying to loop through each key but i am facing a problem of same value repeating inside for each loop
Please be noted we should keep on same code structure multiple foreach it's requirement i already posted this question here but didn't get solution instead of solution entire new code as answer imposed on me and nobody is actually taking care of it
Here is example of my current code and result (click here)
here is my code so far
<?php
$data2 = array(
'category_name' => '33287*100*prescription*1,32457*1250*lab*1'
);
$result = array('0' => (object)$data2);
foreach ($result as $key => $category) {
$category_name = explode(',', $category->category_name);
}
$newresults=[];
foreach ($category_name as $key) {
$category->category_name = $key;
$newresults[]=$category;
}
$result=$newresults;
$newresults=[];
$category->items_count = 0;
foreach ($result as $key => $value) {
list($sale_key, $sale_value) = explode('*', $value->category_name);
// $category->items_count += count($sale_value);
$newresults[]=$category;
}
$result=$newresults;
i am getting the wrong results like this
Array
(
[0] => stdClass Object
(
[category_name] => 33287*100*prescription*1
)
[1] => stdClass Object
(
[category_name] => 33287*100*prescription*1
)
)
As noted, because you are reusing variable names, and also using them when their scope might not be correct or accepted, you are causing some confusion.
The code below brings the bottom loop inside of the top loop, because that's where the context really lives. Creating a temporary loop only adds to the potential confusion. If that doesn't work with the additional logic, more changes will be needed. I also changed a bunch of the variable names to hopefully make things more obvious. See the comments in the code for more details.
$reporting_data = array(
'category_name' => '33287*100*prescription*1,32457*1250*lab*1,32459*1500*lab*1,32460*400*lab*1,32461*600*lab*1,32468*950*lab*1,32470*950*lab*1,33291*2500*lab*1,33292*2500*lab*1,47516*2000*lab*1,49209*0*lab*1,56835*2400*lab*1,56836*2400*lab*1',
'patient' => '28370',
'date' => 1643030497,
'ref' => '371',
);
// Create array of objects
$reporting_data_as_objects[] = (object)$reporting_data;
$results = [];
foreach ($reporting_data_as_objects as &$obj) {
// Setup base data that is shared across all items
$obj->reception_data_sum = 0;
$obj->references_data_sum = 0;
$obj->actual_price = 0;
$category_names = explode(',', $obj->category_name);
// Loop over the comma-delimited parts of category_name
foreach ($category_names as $category_name) {
// Clone our template object
$tmp = clone $obj;
// The second item of the asterisk-delimted field is the price
// We used $_ to indicate that we aren't interested in the first item.
list($_, $sale_value) = explode('*', $category_name);
// Set object-specific fields on our clone
$tmp->category_name = $category_name;
$tmp->actual_price = (int)$sale_value;
// Add the clone to the array
$results[] = $tmp;
}
}
// Always unset by-ref variables of a foreach
unset($obj);
print_r($results);
Demo here: https://3v4l.org/95KAQ

Can't push all array items into a second array PHP

So I have an array ($items) which has about 900 items in it. What I'm trying to do is, for the items that are read ($key["status"] == 1) which is about 300 items -> push those into a second array ($lifeSpanArray) with two attributes (added_date and read_date).
For some reason, when I try to push items into the lifespan array, I only have one item. Like I said, there are around 300 items that are status read - and I can dump those out, so I believe I am making a mistake with building my lifeSpanArray and pushing into it.
Any help much appreciated!
$items = $pocket->retrieve($params, $accessToken);
$numberArticles = count($items["list"]);
$hasRead = 0;
$hasNotRead = 0;
$readDatesArray = array();
$lifeSpanArray = array();
foreach ($items['list'] as $key) {
if ($key["status"] == 1) {
$hasRead++;
$timeAdded = date('m/d/Y', $key["time_added"]);
$dateRead = date('m/d/Y', $key["time_read"]);
// Where the problem is - only one item added
$lifeSpanArray['timeAdded'] = $timeAdded;
$lifeSpanArray['timeRead'] = $dateRead;
//Push into the read dates array
array_push($readDatesArray, $dateRead);
}
else {
$hasNotRead++;
}
}
var_dump($lifeSpanArray);
As you are overwriting your $lifeSpanArray array on each iteration you're must be getting only last entry so what you need is a two-dimension array,
Change this,
//Push into lifespan array
$lifeSpanArray['timeAdded'] = $timeAdded;
$lifeSpanArray['timeRead'] = $dateRead;
to,
$lifeSpanArray[] = array('timeAdded' => $timeAdded,'timeRead' => $dateRead);
$lifeSpanArray['timeAdded'] = $timeAdded;
$lifeSpanArray['timeRead'] = $dateRead;
For the above code, you are actually assigning a scalar value to $lifeSpanArray['timeAdded'] and $lifeSpanArray['timeRead'].
To treat them as array and push values to them, you should first initialize timeAdded and timeRead as arrays first:
$lifeSpanArray = array(
'timeAdded' => array(),
'timeRead' => array()
);
And pushing values to them within the foreach loop:
$lifeSpanArray['timeAdded'][] = $timeAdded;
$lifeSpanArray['timeRead'][] = $dateRead;

Add array to itself in foreach without duplicating?

public function GetOpsPremiums()
{
// Get the Cost Multiplier
$costMulti = $this->GetCostMultiplier();
// Get the Prem Ops
$premOps = $this->GetPremOpsEL();
// Get the Factors
$factors = $this->GetFactors();
// Get the full class array
$classArray = $this->GetClassArray();
foreach ($classArray as $key => $values) {
$classTotalHalved = $values / 1000;
$mainMultiplier = $costMulti * $premOps[$key] * $factors[$key]['premops'];
$premium = $classTotalHalved * $mainMultiplier;
$opsPremiums = array(
$key => round($premium)
);
}
return $opsPremiums;
}
I want $opsPremiums to not just iterate 1 at a time. I need it to iterate and add itself to itself.
I tried
foreach ($opsPremiums as $key2 => $values2) {
$opsPremiums = array(
$key => round($premium)
);
}
Can someone explain to me what I need to do in order to get the $opsPremium to stack itself neatly into a single array?
should be so
foreach ($opsPremiums as $key2 => $values2) {
$opsPremiums[] = array(
$key => round($premium)
);
}
I'm a little unsure of what you mean, but are you trying to return all the results in $opsPremium?
The issue is your setting it as an array on each iteration rather than adding to it.
$opsPremium = array();
foreach ($classArray as $key => $values) {
$classTotalHalved = $values / 1000;
$mainMultiplier = $costMulti * $premOps[$key] * $factors[$key]['premops'];
$premium = $classTotalHalved * $mainMultiplier;
$opsPremiums[] = array(
$key => round($premium)
);
}
return $opsPremiums;
Storing $opsPremium outside the loop and adding to it each time will do that for you.
My apologies if this is not what you were asking.
It seems your very confused with the terminology of how to speak about your problem. I believe what you should have asked is, I want to add some extra items including itself to an array for return. In which case the magic you need is array concatenation. Im a little unsure of what your array requires, but you can append anything to any php array with the [] operator. If you respond with a more detailed question perhaps I can help you further.
Depending on what your are adding to your array, for the program to function you may need to use the array_merge, array_push.

PHP array copy certain keys, built-in functions? Nested loop performance?

I have a PHP array that I'd like to duplicate but only copy elements from the array whose keys appear in another array.
Here are my arrays:
$data[123] = 'aaa';
$data[423] = 'bbb';
$data[543] = 'ccc';
$data[231] = 'ddd';
$data[642] = 'eee';
$data[643] = 'fff';
$data[712] = 'ggg';
$data[777] = 'hhh';
$keys_to_copy[] = '123';
$keys_to_copy[] = '231';
$keys_to_copy[] = '643';
$keys_to_copy[] = '712';
$keys_to_copy[] = '777';
$copied_data[123] = 'aaa';
$copied_data[231] = 'ddd';
$copied_data[643] = 'fff';
$copied_data[712] = 'ggg';
$copied_data[777] = 'hhh';
I could just loop through the data array like this:
foreach ($data as $key => $value) {
if ( in_array($key, $keys_to_copy)) {
$copied_data[$key] = $value;
}
}
But this will be happening inside a loop which is retrieving data from a MySQL result set. So it would be a loop nested within a MySQL data loop.
I normally try and avoid nested loops unless there's no way of using PHP's built-in array functions to get the result I'm looking for.
But I'm also weary of having a nested loop within a MySQL data loop, I don't want to keep MySQL hanging around.
I'm probably worrying about nested loop performance unnecessarily as I'll never be doing this for more than a couple of hundred rows of data and maybe 10 keys.
But I'd like to know if there's a way of doing this with built-in PHP functions.
I had a look at array_intesect_key() but that doesn't quite do it, because my $keys_to_copy array has my desired keys as array values rather than keys.
Anyone got any ideas?
Cheers, B
I worked it out - I almost had it above.I thought I'd post the answer anyway for completeness. Hope this helps someone out!
array_intersect_key($data, array_flip($keys_to_copy))
Use array_flip() to switch $keys_to_copy so it can be used within array_intersect_keys()
I'll run some tests to compare performance between the manual loop above, to this answer. I would expect the built-in functions to be faster but they might be pretty equal. I know arrays are heavily optimised so I'm sure it will be close.
EDIT:
I have run some benchmarks using PHP CLI to compare the foreach() code in my question with the code in my answer above. The results are quite astounding.
Here's the code I used to benchmark, which I think is valid:
<?php
ini_set('max_execution_time', 0);//NOT NEEDED FOR CLI
// BUILD RANDOM DATA ARRAY
$data = array();
while ( count($data) <= 200000) {
$data[rand(0, 500000)] = rand(0, 500000);
}
$keys_to_copy = array_rand($data, 100000);
// FOREACH
$timer_start = microtime(TRUE);
foreach ($data as $key => $value) {
if ( in_array($key, $keys_to_copy)) {
$copied_data[$key] = $value;
}
}
echo 'foreach: '.(microtime(TRUE) - $timer_start)."s\r\n";
// BUILT-IN ARRAY FUNCTIONS
$timer_start = microtime(TRUE);
$copied_data = array_intersect_key($data, array_flip($keys_to_copy));
echo 'built-in: '.(microtime(TRUE) - $timer_start)."s\r\n";
?>
And the results...
foreach: 662.217s
array_intersect_key: 0.099s
So it's much faster over loads of array elements to use the PHP array functions rather than foreach. I thought it would be faster but not by that much!
Why not load the entire result set into an array, then begin processing with nested loops?
$query_result = mysql_query($my_query) or die(mysql_error());
$query_rows = mysql_num_rows($query_result);
for ($i = 0; $i < $query_rows; $i++)
{
$row = mysql_fetch_assoc($query_result);
// 'key' is the name of the column containing the data key (123)
// 'value' is the name of the column containing the value (aaa)
$data[$row['key']] = $row['value'];
}
foreach ($data as $key => $value)
{
if ( in_array($key, $keys_to_copy))
{
$copied_data[$key] = $value;
}
}

PHP loop, date sorting challenge

I'm having trouble trying to figure out how to achieve this programming challenge in my Zend Framework application:
I need to create an array that looks like this:
$array = array(
0 => stdClass()->monthName
->monthResources = array()
1 => stdClass()->monthName
->monthResources = array()
);
This is the original array I have to work with:
$resources = array(
0 => Resource_Model()->date (instance of Zend_Date)
1 => Resource_Model()->date
2 => Resource_Model()->date
//etc...
);
The original array ($resources) is already sorted by date (descending) so I need to create an array where the resources are grouped by month. I only want the months that have resources, so if the resources skip a month, there shouldn't be a stdClass object for that month in the final array.
I also want this to process quickly, so any advice on optimizing the code (and still being readable) would be great. How can I achieve this?
My offering. No guarantees on its speed however it is O(n) and should in theory be faster than your method. This may not be true in any or all cases. However if you want something optimized you should have used a profiler to ensure that this is the function that is causing the speed problems rather than trying to make sections of code fast when they only account for .001% of the execution time. (In which case the maximum gain from optimizing the function would be .001%)
$resources = $this->fetchAll();
$sortedresources = array();
foreach ($resources as $resource) {
$monthName = $resource->getDate()->get(Zend_Date::MONTH_NAME);
if ( !isset($sortedresources[$monthName]) ){
//setup new data for this month name
$month = new stdClass();
$month->name = $monthName;
$month->monthResources = array();
$sortedresources[$monthName] = $month;
}
$sortedresources[$monthName]->monthResources[] = $resource;
}
//return the values of the array, disregarding the keys
//so turn array('feb' => 'obj') to array(0 => 'obj)
return array_values($sortedresources);
Maybe this helps (pseudo-code)
$finalArray = new array();
$tempStdClass = null;
foreach ($resObj in $resources)
{
if ($tempStdClass == null)
$tempStdClass = new StdClass($resObj->date);
if (tempStdClass->monthName != $resObj->date)
{
array_push($finalArray, $tempStdClass);
$tempStdClass = new StdClass($resObj->date);
}
array_push($tempStdClass->monthResources, $resObj);
}

Categories