I am trying to get different sums depending on certain conditions, there needs to be 20 different variables which will be returned at the end of the function. I feel like there should be an easier way to do it? Thanks for any help in advanced.
function getTotalNutrients($id)
{
$applications = $this->getApplications($id);
$nsum = 0;
$psum = 0;
$ksum = 0;
$mgsum = 0;
// ...
foreach( $applications as $pro ) {
$details = $this->getAppliedNutrients($pro->id);
foreach($details as $nutrients){
switch($pro->area){
case "1":
$nsum += ($nutrients->n);
$psum += $nutrients->p;
$ksum += $nutrients->k;
$mgsum += $nutrients->mg;
break;
case "2":
$tnsum += ($nutreients->n);
// ...
break;
case "3":
$fnsum += ($nutreients->n);
//...
break;
case "4":
// ...
break;
case "5":
// ...
break;
}
}
}
return array(
$nsum,
$psum,
$ksum,
$mgsum,
$tnsum,
// etc..
);
}
create an array by adding all values like
$arraySum[] = array();
$arraySum[] = $nsum;
$arraySum[] = $tnsum;
.
.
.
ect
at the end
use
array_sum($arraySum);
If it is not fullfill your requirement. provide your code completely.
You have a case of design patterns here my friend...
Instead of using a large switch (breaks the Open/Closed principle) you should create an interface that calculates the different sums and returns a tupple (set of different values) of your choice.
Then create those AREAS of yours following the interface details. Once all your areas calculators are created, just create each of them and put them in an array based on the id. Then, load the right one using the id and calculate your values returned in a tupple...
Your code will look much tidier!
Related
I am working on a project that requires me to generate a report based on purchased rentals. I have to keep a count for each type of rental and sum the corresponding totals. Currently I am using a switch block to determine which action to take based on the current rental. However, it is my understanding that this violates the Open/Closed Principle, as I will have to modify the switch block every time a new rental is added. I would like to make this meet the OCP, but I am not sure how to go about. Below is my code:
public function generateReport($rentals)
{
$type_1_count = 0;
$type_2_count = 0;
$type_3_count = 0;
$type_1_sum = 0;
$type_2_sum = 0;
$type_3_sum = 0;
foreach ($rentals as $rental) {
switch ($rental->type) {
case 'TYPE_1':
$type_1_count++;
$type_1_sum += $rental->price;
break;
case 'TYPE_2':
$type_2_count++;
$type_2_sum += $rental->price;
break;
case 'TYPE_3':
// some of the rentals include other rentals which must be accounted for
$type_1_count++;
$type_1_sum += $rental->price / 2;
$type_3_count++;
$type_3_sum += $rental->price / 2;
break;
default:
echo 'Rental Not Identified';
}
}
return compact('type_1_count', 'type_2_count', 'type_3_count', 'type_1_sum', 'type_2_sum', 'type_3_sum');
}
I am modifying the shared state variables depending on the selected case. I reviewed many OCP examples but all of them show how to execute an action or return a value, but I need to modify the shared state instead. What would be the best way to refactor this code to be more inline with the OCP?
You can use an associative array. You just need to make sure the variable is set first. Something like this:
$all_rentals = [];
foreach ($rentals as $rental) {
// make sure the variable has a default value
if(!isset($all_rentals[$rental->type."_count"]) {
$all_rentals[$rental->type."_count"] = 0;
$all_rentals[$rental->type."_sum"] = 0;
}
$all_rentals[$rental->type."_count"]++;
$all_rentals[$rental->type."_sum"] += $rental->price;
}
...
This way you can add new values (rental types) without having to modify any existing code
I have a XML file which was generated by an external entity that I want to parse using SimpleXML. My problem is that in the mapping given by the client I have some conditions in it to get the info I want.
For example, the mapping for the client code is something like this: E1ADRM1\PARTNER_Q=OSO\E1ADRE1\EXTEND_D which means that the code for the client is the value of the EXTEND_D tag, which is nested in one of the many PARTNER_Q tags. The one that has the OSO value.
I am starting today to explore SimpleXML, so I have no idea how to get this info.
For what I've read so far, it is pretty simple to get the info of a node, accessing it's properties. If I a single PARTNER_Q and no condition, my $clientCode would be $xml->E1ADRM1->PARTNER_Q->E1ADRE1->EXTEND_D (right?)
Any hint on how can I get the info having that PARTNER_Q=OSO condition in mind?
For future reference, I'll leave here the code with how I've solved this.
I've Based my code on this well written answer that I found.
$root = $xml->IDOC;
$shippingPoints = array();
// Go through the list of OTs
foreach($root->E1EDL20 as $ot) {
// Instanciate empty OT
$otInfo = emptyOt();
$otInfo["refOt"] = trim($ot->VBELN);
// Go through partner to get the wanted items
foreach ($ot->E1ADRM1 as $partner) {
switch ($partner->PARTNER_Q) {
case "OSO":
$otInfo["codeLoader1"] = trim($partner->E1ADRE1->EXTEND_D);
break;
case "OSP":
$otInfo["refShip"] = trim($partner->PARTNER_ID);
// get the shipping info if not already fetched
if(!isset($shippingPoints[$otInfo["refShip"]])) {
$shippingPoints[$otInfo["refShip"]] = Whouses::getWhouseAddressByCode($otInfo["refShip"]);
}
// assign values
$otInfo["brandNameShip"] = $shippingPoints[$otInfo["refShip"]]["name"];
$otInfo["addrShip1"] = $shippingPoints[$otInfo["refShip"]]["address"];
$otInfo["cityShip"] = $shippingPoints[$otInfo["refShip"]]["city"];
$otInfo["zipShip"] = $shippingPoints[$otInfo["refShip"]]["zipcode"];
$otInfo["countryShip"] = $shippingPoints[$otInfo["refShip"]]["country"];
break;
case "WE":
$otInfo["refDeliv"] = trim($partner->PARTNER_ID);
$otInfo["addrDeliv1"] = trim($partner->STREET1);
$otInfo["cityDeliv"] = trim($partner->CITY1);
$otInfo["zipDeliv"] = trim($partner->POSTL_COD1);
$otInfo["countryDeliv"] = trim($partner->COUNTRY1);
default:
// do nothing
break;
}
}
// Go through the dates info to get the wanted items
foreach ($ot->E1EDT13 as $qualf) {
switch ($qualf->QUALF) {
case "006":
$dtDeliv = trim($qualf->NTANF);
$timeDeliv = trim($qualf->NTANZ);
$otInfo["initialDtDeliv"] = convertToOtDateFormat($dtDeliv, $timeDeliv);
break;
case "007":
$initialDtShip = trim($qualf->NTANF);
$timeInitialDtShip = trim($qualf->NTANZ);
$otInfo["initialDtShip"] = convertToOtDateFormat($initialDtShip, $timeInitialDtShip);
break;
default:
// do nothing
break;
}
}
}
Hope this'll help someone!
I have a pretty complicated array operation, or at least it is complicated for me. Lets say I got such kind of array
$myArr['url_1']['linktypes']['follow'] = 10;
$myArr['url_1']['linktypes']['nofollow'] = 20;
$myArr['url_1']['linktypes']['other'] = 30;
$myArr['url_2']['linktypes']['follow'] = 40;
$myArr['url_2']['linktypes']['nofollow'] = 50;
$myArr['url_2']['linktypes']['other'] = 60;
$myArr['url_3']['linktypes']['follow'] = 70;
$myArr['url_3']['linktypes']['nofollow'] = 80;
$myArr['url_3']['linktypes']['other'] = 90;
and simply (!) I need to get following result
array(
array("id"=>1,"metric"=>'follow','url_1'=>10,'url_2'=>40,'url_3'=>70),
array("id"=>2,"metric"=>'nofollow','url_1'=>20,'url_2'=>50,'url_3'=>80),
array("id"=>3,"metric"=>'other','url_1'=>30,'url_2'=>60,'url_3'=>90)
);
These array elements are created dynamicall from $myArr. I have tried many ways but I failed many times. Hopefully someone has a short, simple logic to solve this.
Thanks.
Edit: This one is my shortest try. I have many different ways but this code is a part of big code structure, example you see here is created to simplify the logic I need.
$linkStructure = array();
foreach($myArr as $links=>$value){
$counter = 0;
foreach($value['linktypes'] as $ltKey => $ltValue){
if($linkStructure[$counter]["id"] && $linkStructure[$counter]["metric"] == $ltKey){
$linkStructure[$counter][$links] = $ltValue;
}
else{
$linkStructure[$counter]["id"] = $counter;
$linkStructure[$counter]["metric"] = $ltKey;
$linkStructure[$counter][$links] = $ltValue;
}
}
}
I swear I tried how I can prove better I don't know. Don't torture please. If you have any idea just share, is it too much I'm asking for?
I can't explain this very well, so I'm just going to use code:
$fixedArr = array();
$idCount = 1;
foreach($myArr as $title=>$subArr)
foreach($subArr['linktypes'] as $metric=>$val) {
if(!array_key_exists($metric)) {
$fixedArr[$metric] = array();
$fixedArr[$metric]['id'] = $idCount;
$fixedArr[$metric]['metric'] = $metric;
$idCount += 1;
}
$fixedArr[$metric][$title] = $val;
}
That should do it.
But I should say that the comments are right, a better way to structure your array would be like this:
Array {
[url_*] => Array {
[metric] => someValue;
}
}
Basically the same way you have it originally, but with fewer dimensions. This is all that you need for the data you have provided.
So I have this for loop with 36 if-queries inside. Any advice on making it more efficient?
You can view the complete code here
Here's a sample of what it looks like:
$numbers = range(1, 36);
shuffle($numbers);
for ($m =0; $m<37; $m++){
if ($numbers[$m] == "1"){
$mirza[$m] = "RUTHIE";
$mage[$m] = "3";
$mquote[$m] = "I get to learn a lot of new things here, like sign language!";
$link[$m] = "http://www.google.com";
}
if ($numbers[$m] == "2"){
$mirza[$m] = "AIDA";
$mage[$m] = "82";
$mquote[$m] = "This is like a club and I know and like all the members. It's good therapy.";
$link[$m] = "/about/";
}
if ($numbers[$m] == "3"){
$mirza[$m] = "AMIRE";
$mage[$m] = "4";
$mquote[$m] = "I am learning how to share and make friends.";
$link[$m] = "/about/";
}
}
Changing your if-s to a switch with appropriate breaks or even at worse if..elseif..etc would mean that not all ifs have to be evaluated.
When you have this much data it really should be in a database (maybe XML or document style) or a text file.
Still, I'll show you a way to improve this by hard-coding all the data in one place and eliminating the need for any if or switch statements. You should AT VERY LEAST change to switch statements, better still hard-code all the data in one place (below), better still get a database or CSV text file storing these values.
$data = array(
array('mirza' => 'RUTHIE', 'mage' => '3', 'mquote' => 'I get to learn a lot of new things here, like sign language!', 'link' => 'http://www.google.com'),
array(......
);
$numbers = range(1, 36);
shuffle($numbers);
for ($m =0; $m<37; $m++){
$index = $numbers[$m];
$mirza[$m] = $data[$index]['mirza'];
$mage[$m] = $data[$index]['mage'];
$mquote[$m] = $data[$index]['mage'];
$link[$m] = $data[$index]['link'];
}
Just to be clear, the 'efficiency' issue here isn't so much speed (though this way is faster). Its that what you have is a nightmare to maintain and change.
You might do better to seriously reconsider the design of your app, specifically making an object with mirza, mage, mquote, link as fields.
Is your problem that you feel you simply have too many "if" statements? Try a switch statement. Technically speaking, your loop is about as efficient as possible (using big-O method of evaluating efficiency).
for ($m =0; $m<37; $m++){
switch($numbers[$m])
{
case 1:
$mirza[$m] = "RUTHIE";
$mage[$m] = "3";
$mquote[$m] = "I get to learn a lot of new things here, like sign language!";
$link[$m] = "http://www.google.com";
break;
case 2:
$mirza[$m] = "AIDA";
$mage[$m] = "82";
$mquote[$m] = "This is like a club and I know and like all the members. It's good therapy.";
$link[$m] = "/about/";
break;
case 3:
$mirza[$m] = "AMIRE";
$mage[$m] = "4";
$mquote[$m] = "I am learning how to share and make friends.";
$link[$m] = "/about/";
break;
}
}
My guess it's already as fast as you may ever need.
The only way to make it faster is to look at sites benchmarking PHP and to adjust this or that to easy the most efficent PHP internals.
Since PHP internals may change over time (with PHP updates), unless you regulary benchmark your code I'd say you have not that much to gain, especially with code that simple.
For example you may try to change
$numbers[$m] == "2"
with
$numbers[$m] === 2
or
$numbers[$m] === "2"
But if you don't benchmark your code (ex. putting it in a function and measure the execution time over 10.000 iterations) you're gonna be blind about improvements. PHP internals speed don't always behave as you expect it.
(the same goes with the answer you got about the switch statement. Try it, but benchmark !)
My advice is to try to work with data structures, and put all these values in an array of the same type as the data structure. That way, if you later need to access the values again with the same number-to-value relationship, you won't have to redefine everything like you have done here. All you would have to do then is to use array indices to access the values.
Could I achieve this in a more cleaner way? Would be really appricated.
switch($vocation) {
case 1: $vocation = "Sorcerer"; break;
case 2: $vocation = "Druid"; break;
case 3: $vocation = "Paladin"; break;
case 4: $vocation = "Knight"; break;
case 5: $vocation = "Master Sorcerer"; break;
case 6: $vocation = "Elder Druid"; break;
case 7: $vocation = "Royal Paladin"; break;
case 8: $vocation = "Elite Knight"; break;
}
Thanks!
and sorry about the title couldnt figure out a better one
You can use an array instead of the switch statement.
$vocations = array("Sorcerer", "Druid" ...);
$vocationStr = $vocations[$vocation - 1];
Or
$vocations = array(1 => "Sorcerer", 2 => "Druid" ...);
$vocationStr = $vocations[$vocation];
Personally, I am against the reuse of $vocation to refer to two types (integers and String), as they are most likely two different concepts in your program.
I would also advise creating an Enum type for these values instead, but this depends on the context.
Well, depending on what you're using this for (not really sure as there is no context) one option is to have your vocations available in a configuration file (be it XML, text file, etc) that can be read in by the system on the fly. This way, if you ever want to add an additional vocation, you can just update the text file and you don't need to rebuild and/or redeploy your source.
Either using an array as already mentioned, or, at the very least, using constatants rather than the plain integers in the switch statement. So something like:
define("SORCERER", 1);
define("DRUID", 2);
...
define("RPALADIN", 7);
define("EKNIGHT", 8);
switch($vocation) {
case SORCERER: $vocation="Sorcerer"; break;
case DRUID: $vocation="Druid"; break;
...
case RPALADIN: $vocation="Royal Paladin"; break;
case EKNIGHT: $vocation="Elder Knight"; break;
}
That way adding new classes and ranks between classes is a little easier.
This gives a trap for initial values of vocation not found in your list.
$vocations = array(1=>"Sorcerer", 2=>"Druid", ...);
if (array_key_exists($vocation, $vocations)) {
$vocation = $vocations[$vocation];
} else {
$vocation = 'Not found';
}
Use an enumeration
http://it.toolbox.com/blogs/macsploitation/enums-in-php-a-native-implementation-25228
An array can work for sequential keys, but use an associative array (hash) otherwise.
%vocations = array("key1"=>"value1", "key2"=>"value2", ...);
$vocationVal = $vocations{$vocationKey};
EDIT: The above is a perl syntax, the php syntax is below:
$vocations = array(key1 => "value1", key2 => "value2", ...);
$vocationVal = $vocations[$vocationKey];
Another option that i haven't seen here is to keep the data in a table. It will help if the list becomes huge.
vocation_id vocation_name
1 sorcerer
2 druid
etc....
select vocation_name form vocations where vocation_id=3