I have this situation:
The User can choose between 3 products, A, B or C, like showed in the image. After the user clicks one of the products, he is redirected to a register form where he must include some data, including x and y. If the selected product and the value of x and y are not right, an error is launched. If the entered data is correct, then some other actions are done. I've tried to implement this control, but I'm not sure this is the best solution.
if ($product==("A") && x < 10 && y <= 2)
{
price = 10;
}
else if ($product ==("B") && x < 50 && y <= 10 && y >2)
{
price = 20;
}
else if ($product ==("C") && x < 250 && y <=50)
{
price = 30;
}
The "object oriented" approach here would be to avoid the "tell don't ask" pattern that you implemented.
Meaning: you are "asking" for certain properties; so that your code can make a decision based on that. The solution to that is: don't do it that way!
Instead: you create a "base" product class which offers methods like isXinRange() and isYinRange(). Then you have different sub classes for each product; and AProduct.isXinRange checks x < 10 ...
Meaning: your range checks go into three different classes!
And instead of putting everything into "one" comparison, you do something like:
You create an object of AProduct for "A", BProduct for "B", ... and so on; like someProduct = generateProductFor(stringFromUser)
Then you simply ask if someProduct.isXinRange() gives you true for the X provided by the user
(I am not too familiar with PHP, so sorry for my half-pseudo-half-java coding style here)
//not a short solution or a fast one but has some advantages:
//1. has some logic shared by GhostCat but in a interative way
//2. takes in consideration of multiple cases of A,B,C... in case you load your data from DB
//3. separates raw data from logic, easy to edit later and expand
$data = array(
'A' => array(
'variables' => array(
'x' => array(
10 => '<'
),
'y' => array(
2 => '<='
),
),
'function' => 'functionA',
),
'B' => array(
'variables' => array(
'x' => array(
50 => '<'
),
'y' => array(
2 => '>'
),
),
'function' => 'functionB',
),
'C' => array(
'variables' => array(
'x' => array(
250 => '<'
),
'y' => array(
50 => '<='
),
),
'function' => 'functionC',
),
);
//
foreach ($data[$product]['variables'] as $variable => $variable_data) {
foreach ($variable_data as $number => $operator) {
switch ($operator) {
case '<':
if (!($variable < $number)) {
myFailFunction();
}
break;
case '<=':
if (!($variable <= $number)) {
myFailFunction();
}
break;
case '>':
if (!($variable < $number)) {
myFailFunction();
}
break;
}
}
}
//if no fail was met run attached function
$func_name = $data[$product]['function'];
$func_name();
//it should run like this too
//$data[$product]['function']();
Related
I have an array with key and value pair. I'm building this array dynamically and below is the code.
$x[] = array('type_name' => $value->name,
'percentage'=> intval($percentage));
My intention is to get the maximum value and for that I do
max($x);
However it is returning the wrong value actually the lowest value. Following is my array. Any help would be awesome.
$x = array(
array(
'type_name' => 'type 1'
'percentage' => 10,
),
array(
'type_name' => 'type 2'
'percentage' => 15,
),
array(
'type_name' => 'type 3'
'percentage' => 45,
),
);
Thanks is advance.
From php max() documentation :
// Multiple arrays of the same length are compared from left to right
It means that if you want to compare "percentage" values first instead of "type_name" values, you'll have to change their order in the array.
So, you could build your array like this ("percentage" comes first) and it should work :
$x[] = array(
'percentage'=> intval($percentage),
'type_name' => $value->name
);
For example :
$x = array(
array(
'percentage' => 10,
'type_name' => 'type 1'
),
array(
'percentage' => 15,
'type_name' => 'type 2'
),
array(
'percentage' => 45,
'type_name' => 'type 3'
),
array(
'percentage' => 25,
'type_name' => 'type 4'
)
);
print_r(max($x));
Output :
Array
(
[percentage] => 45
[type_name] => type 3
)
Hope it helps.
You need to read how the max compares against different types of data. In your case, you are trying to compare against one of the array item i.e. percentage inside one of the item so the function max does not know to do this.
There is an example by Revo in the manual which shows you how to do this.
You are creating an array of arrays. max doesn’t know that your arrays should be compared by the 'percentage' key, so it can’t be used here.
Instead, find the maximum value yourself. For example, like this:
$maxPercentage = false;
foreach ($x as $item) {
if ($maxPercentage === false || $item['percentage'] > $maxPercentage) {
$maxPercentage = $item['percentage'];
}
}
Now, $maxPercentage will store maximum percentage. Of, if you want an item with maximum percentage, get it like this:
$maxPercentage = false;
$maxItem = false;
foreach ($x as $item) {
if ($maxPercentage === false || $item['percentage'] > $maxPercentage) {
$maxPercentage = $item['percentage'];
$maxItem = $item;
}
}
I am creating a conversion table using PHP and I have to check the user's input against A LOT of scenarios and I started by using if statements but this doesn't seem to be efficient whatsoever and I was hoping for an easier way to go through all the scenarios.
I looked into ternary and switch options but those don't seem to do what I need it to do and I also considered array's (the option I think I need to use)
What I'm trying to do:
The user enters in a grade level and scores for a category. Based on the sum of those scores and the grade level, I need to compare them to get two other scores
Example code:
if ($grade == 1 && $sumScore <= 5)
{
$textscore = 'Beginning';
}
if ($grade ==1 && ($sumScore>5 && $sumScore <=8))
{
$textScore = 'Intermediate';
}
etc....
There are 13 grades (K-12) and 4 categories I need to go through all with their own "raw scores" to consider to get these other scores. How can I avoid using a ton of If/Else if statements?
Thanks!!
You could use a two-dimensional array that's 13x4. Then you can use a nested for loop to go through each possibility and just have one statement that gets run a bunch of times because of the for loops.
For example, the array might look like this:
$textscores = array (
1 => array(5 => 'Beginning', 8 => 'Intermediate', ...),
...
3 => array(5 => 'Intermediate', ...),
...
);
The nested for loop might look like this:
foreach($textscores as $grade => $scores) {
foreach($scores as $sumScore => $textScore) {
if($userGrade == $grade && $userSumScore <= $sumScore) {
$userTextScore = $textScore;
break 2;
}
}
}
I haven't tested this (sorry), but I think something like this
function getTextScore($grade, $sum) {
$rules = array( array("grade" => 1, "minSum" => null, "maxSum" => 5, "textScore" => "Beginning"),
array("grade" => 1, "minSum" => 6, "maxSum" => 8, "textScore" => "Intermediate" ),
/* ... */
);
for ($ruleIdx=0; $ruleIdx<count($rules); $ruleIdx++) {
$currentRule = $rules[$ruleIdx];
if (($currentRule['grade'] == $grade) &&
((is_null($currentRule['minSum'])) || ($currentRule['minSum'] <= $sum)) &&
((is_null($currentRule['maxSum'])) || ($currentRule['maxSum'] >= $sum))) {
return $currentRule['textScore'];
}
}
// got to the end without finding a match - need to decide what to do
}
The rules have optional min and max values. It will stop as soon as it finds a match, so the order is important. You will need to decide if no rules are matched. You should be able to just drop extra rules in or change the existing ones without changing the logic.
From your example I would suggest the following
Multidimensional array, but a bit different from the way you construct the array
// Grade => [Text => [Min,Max]]
$textScores = [
1 => [
'Beginning' => [0, 5],
'Intermediate' => [5, 8],
'Master' => [8, 10]
],
2 => [
'Beginning' => [0, 7],
'Intermediate' => [7, 8],
'Master' => [8, 10]
],
3 => [
'Beginning' => [0, 3],
'Intermediate' => [3, 6],
'Master' => [6, 10]
]
];
// Random input to test
$grade = rand(1, 3);
$sumScore = rand(0, 10);
foreach ($textScores[$grade] as $Text => $MinMax) {
if ($MinMax[0] <= $sumScore && $MinMax[1] >= $sumScore) {
$textScore = $Grade;
break;
}
}
I am looking into writing a PHP function that uses a convenient method/formula to calculate the dimensions of i.e. a parcel/pallet loaded with items.
Here is an example of an array with items. Note: Some items are flagged to be sent as separate parcels. Some items may not be tilted.
$items = array(
1 => array(
'quantity' => 1,
'weight' = 1,
'dimensions' => array(80, 50, 50), // Length, Width, Height
'separate' => true, // If the item should be sent as a separate package
'tiltable' => false, // False if the item has a 'this side up' sticker
),
2 => array(
'quantity' => 3,
'weight' = 1,
'dimensions' => array(21, 15, 10),
'separate' => false,
'tiltable' => true,
),
3 => array(
'quantity' => 2,
'weight' = 1,
'dimensions' => array(18, 19, 20),
'separate' => false,
'tiltable' => true,
),
// ... and so on ...
);
Does anyone have the slightest bit of knowledge or experience from doing this? I don't want to reinvent the wheel.
The function I have in mind is something like this:
* (Syntax errors may occur) *
function build_packages($items, $max_weight=0, $max_length=0, $max_width=0, $max_height=0) {
$packages = array();
// Step through each item
foreach ($items as $item) {
// Twist and turn item. Longest side first ([0]=length, [1]=width, [2]=height)
if (!empty($item['tiltable'])) {
rsort($item['dimensions'], SORT_NUMERIC);
} else {
if ($item['dimensions'][0] < $item['dimensions'][1]) {
$item['dimensions'] = array($item['dimensions'][1], $item['dimensions'][0], $item['dimensions'][2]);
}
}
// Validate item
if (!empty($max_weight) && $item['weight'] > $max_weight) return false;
if (!empty($max_length) && $item[0] > $max_length) return false;
if (!empty($max_width) && $item[1] > $max_width) return false;
if (!empty($max_height) && $item[2] > $max_height) return false;
// Step through quantities
for ($i=0; $i<$item['quantity']; $i++) {
// Step through packages
$package_found = false;
foreach (array_keys($packages) as $key) {
// Skip to next package on certain conditions
if ($packages[$key]['separate']) continue;
// ...
// Do some logic
// ...
// Modify package
$package_found = true;
$packages[$key]['num_items']++;
$packages[$key]['weight'] += $item['weight'];
$packages[$key]['dimensions'] = array(0, 0, 0); // <--- Replace with new dimensions
// Twist and turn package. Longest side first ([0]=length, [1]=width, [2]=height)
if (!empty($item['tiltable'])) {
rsort($packages[$key]['dimensions'], SORT_NUMERIC);
} else {
if ($packages[$key]['dimensions'][0] < $packages[$key]['dimensions'][1]) {
$packages[$key]['dimensions'] = array($packages[$key]['dimensions'][1], $packages[$key]['dimensions'][0], $packages[$key]['dimensions'][2]);
}
}
break;
}
if ($package_found) continue;
// Add to a new package
$packages[] = array(
'num_items' => 1,
'weight' => $item['weight'],
'dimensions' => $item['dimensions'],
'separate' => $item['separate'],
'tiltable' => $item['tiltable'],
);
}
}
return $packages;
}
Care to help out with some code?
What you are after is a solution to the Bin Packing problem. The solution is NP Hard so you may want to find a "good enough" solution, rather than the optimal solution. A google search turned up this: One Dimensional Bin Packing class. I did not look at the code, so I am not sure how good it is, but I suggest it as a place to at least start your investigation. If it turns out to be good enough for one dimensional problems, perhaps you can create a solution creating multiple 1 dimensional layouts and keep track of each row's max height, therefor knowing whether you have exceeded the total package height.
Hope this helps.
I found this Package https://github.com/dvdoug/BoxPacker and this service https://3dbinpacking.com/ so far that does this for you.
I have the following values from a database call that I want to apply some logic to. I thought I could originally use PHP's max however this doesn't appear to be the case.
I have three suppliers of a product. They might not all stock the item I am displaying, and they all offer a different margin, on a product by product basis though, so that is why I can't just say generally supplier 1 is better than supplier 2 etc.
$supplier1Live = 1
$supplier2Live = 1
$supplier3Live = 0
$marginSupplier1 = 20
$marginSupplier2 = 40
$martinSupplier3 = 50
In this example I would want to use Supplier 2 as they stock the product supplier2Live = 1 and also have the better margin than the other supplier who stocks the product (supplier1)
My mind however is drawing a complete blank in how to code this?
I thought I could add it to an array giving:
$array = array(
"supplier1" => array(
"live" => 1,
"margin" => 20
),
"supplier2" => array(
"live" => 1,
"margin" => 40
),
"supplier3" => array(
"live" => 0,
"margin" => 50
)
);
And run something on that, but not sure what to.
Filter the array using array_filter (filter by live==1), and then find the maximum out of the resultant array (maximum on the "margin" value)
Like this, if I understand correctly
$array = array(
"supplier1" => array(
"live" => 1,
"margin" => 20
),
"supplier2" => array(
"live" => 1,
"margin" => 40
),
"supplier3" => array(
"live" => 0,
"margin" => 50
)
);
$res = array_filter($array,function($v){return $v["live"];});
$supplier = array_reduce($res, function($a, $b){
return $a["margin"]>$b["margin"]?$a:$b;
});
print_r($supplier);
Try something like this:
$best_supplier = null;
$best_supplier_margin = null;
foreach($array as $name => $supplier) {
if($supplier['live']) {
if($supplier['margin'] > $best_supplier_margin || is_null($best_supplier_margin)) {
$best_supplier = $name;
$best_supplier_margin = $supplier['margin'];
}
}
}
if(is_null($best_supplier)) throw new Exception('No suppliers are live!');
echo $best_supplier;
So you basically want to find the max of supplierXLive * marginSupplierX?
You can also implement a custom compare function and provide it to PHPs usort() function
Every time someone visits my site, I show one of three options (A, B, C). If the user likes the option, he clicks on it. I want to find a way to show the options that receive less clicks less frequently. What's the best way of doing this in PHP?
I am saving the clicks in MongoDB by simply adding a "vote" in an array:
$option[]='a';//one click on option A
$option[]='b';//one click on option B
$option[]='b';//another click on option B
try{
$m=new Mongo();
$c=$m->db->clicks;
$c->save($option);
$m->close();
}
catch(MongoConnectionException $e){ die('Error connecting to MongoDB server. ');}
catch(MongoException $e){ die('Error: '.$e->getMessage());}
This prints:
Array
(
[0] => a
[1] => b
[2] => b
)
Not sure if I understand your question correctly. But if I did, then the following is perhaps a rather naive, and perhaps even verbose way of doing what I think you want to do:
// assume the following fictional values,
// that is, the amount of clicks each option has received thusfar
$clicks = array(
'A' => 10,
'B' => 40,
'C' => 50
);
// what is the total amount of clicks?
$totalClicks = array_sum( $clicks );
// determine the lower bound percentages of the option clicks
$clickPercentageBounds = array(
'A' => 0,
'B' => ( ( $clicks[ 'A' ] + 1 ) / $totalClicks ) * 100,
'C' => ( ( $clicks[ 'A' ] + $clicks[ 'B' ] + 1 ) / $totalClicks ) * 100
);
// get random percentage
$rand = mt_rand( 0, 100 );
// determine what option to show, based on the percentage
$option = '';
switch( true )
{
case $rand < $clickPercentageBounds[ 'B' ]:
$option = 'A';
break;
case $rand < $clickPercentageBounds[ 'C' ]:
$option = 'B';
break;
default:
$option = 'C';
break;
}
var_dump( $option );
You CAN do it with PHP and a db, but you might prefer using Google Website Optimizer, I think it offers that option and works quite well.