The job to be done is show the price of postage per KG. So starting at 1KG, I want to increase by 0.50 for every KG.
I tried doing it like this which doesn't seem to work for me:
$shipping_second_class = array (
array ('weight' => range(1,5), 'cost' => range(1,3, 0.50))
);
foreach ($shipping_second_class as $shipping_second_class) {
echo('weight '.$shipping_second_class['weight'].' costs £'.$shipping_second_class['cost'].'<br/>');
}
That doesn't seem to work. What I'm trying to do in a way that's easier to maintain is something like this, but with less code:
$shipping_second_class = array (
array ('weight' => '1', 'cost' => '1'),
array ('weight' => '2', 'cost' => '1.5'),
array ('weight' => '3', 'cost' => '2'),
array ('weight' => '4', 'cost' => '2.5'),
array ('weight' => '5', 'cost' => '3'),
);
foreach ($shipping_second_class as $shipping_second_class) {
echo('weight '.$shipping_second_class['weight'].' costs £'.$shipping_second_class['cost'].'<br/>');
}
Another simple way
$per_kg_extra_cost = 0.5;
foreach (range(0, 4) as $shipping) {
$cost = 1 + ( $shipping * $per_kg_extra_cost );
$weight = ++$shipping;
echo '<pre>';
echo 'Weight: '. $weight . ' cost : ' .$cost;
}
Here's one way to do this:
$shipping_second_class = array (
array ('weight' => 1, 'cost' => 1)
);
for($x = 2; $x < 6; $x++){
$newdata = ['weight' => $x, 'cost' => 0.5 + $x * 0.5];
array_push($shipping_second_class, $newdata);
}
foreach ($shipping_second_class as $s) {
echo('weight '.$s['weight'].' costs £'.$s['cost'].'<br/>');
}
Essentially, you need to add to the array after it's been called rather than initialize it fully filled. You start with the array at 1 value (you could modify this code to start with an empty array) and it expands from there. If you need to add more to the array, just increase the 6 to (Desired Number) + 1 in the for loop.
I'm not sure if there is a way to add to the array in the manner you do in the first block of code.
Here is a simple way to do it:
$key = -1;
$interval = 0.5;
$shipping_second_class = array_map(function($v) use (&$key,$interval) {
$key++;
return ["weight"=>$v,"cost"=>(1 + ($interval * $key))];
},range(1,5));
echo json_encode($shipping_second_class);
You can change the $interval variable if you need to change the way it increments cost
Related
I'm trying to output [device_id] of the least frequent [device_ip_isp] from this array.
Also, If the array only has two SharedDevice Object available, and having different [device_ip_isp], it should output the 2nd SharedDevice Object's [device_id]
array (
0 =>
SharedDevice::__set_state(array(
'device_no' => 1,
'device_id' => '82',
'device_ip_isp' => 'Verizon Fios',
)),
1 =>
SharedDevice::__set_state(array(
'device_no' => 2,
'device_id' => '201',
'device_ip_isp' => 'Spectrum',
)),
2 =>
SharedDevice::__set_state(array(
'device_no' => 3,
'device_id' => '312',
'device_ip_isp' => 'Verizon Fios',
)),
3 =>
SharedDevice::__set_state(array(
'device_no' => 4,
'device_id' => '9715',
'device_ip_isp' => 'Verizon Fios',
)),
4 =>
SharedDevice::__set_state(array(
'device_no' => 5,
'device_id' => '11190',
'device_ip_isp' => 'Verizon Fios',
)),
)
The output should be 201 because "Spectrum" is the least frequent.
I tried the following and had issues:
I'm not sure how I can sort the object variables before comparing to find the least frequent.
/*
$user->getUser_devices() will output the array shown above.
*/
leastFrequent($user->getUser_devices(), 5);
function leastFrequent($arr, $n){
// find the min frequency
// using linear traversal
$min_count = $n + 1;
$res = -1;
$curr_count = 1;
for ($i = 1; $i < $n; $i++) {
if ($arr[$i]['device_ip_isp'] == $arr[$i - 1]['device_ip_isp']) {
$curr_count++;
} else {
if ($curr_count < $min_count) {
$min_count = $curr_count;
$res = $arr[$i - 1]['device_id'];
}
$curr_count = 1;
}
}
// If last element is
// least frequent
if ($curr_count < $min_count) {
$min_count = $curr_count;
$res = $arr[$n - 1]['device_id'];
}
return $arr[$n]$res['device_id'];
}
Ok, below is the explanation of the snippet inside out.
array_column to get all the device_ip_isp values in a single array.
array_count_values to get the frequency of each value.
array_reverse to reverse the count frequency array since you need the latest share device ID incase of count collision for a minimum count value.
min to get the lowest value among all the frequency counts.
array_search to get the key of the first frequency min element.
In the end, we reverse the input array to immediate return the device IP the moment we find the key from the above step matching the current device_ip_isp.
Snippet:
<?php
function getLeastFrequentElement($arr){
$freqs = array_reverse(array_count_values(array_column($arr, 'device_ip_isp')));
$deviceIPISP = array_search(min($freqs), $freqs);
foreach(array_reverse($arr) as $sharedDevice){
if($sharedDevice->device_ip_isp === $deviceIPISP){
return $sharedDevice->device_id;
}
}
throw new Exception("No shared object found!");
}
echo getLeastFrequentElement($arr);
Online Demo
This is the input:
$deals = array(
array('deal' => '1', 'deal_date' => '2017-02-13', 'amount' => '400'),
array('deal' => '2', 'deal_date' => '2017-04-17', 'amount' => '8900'),
array('deal' => '3', 'deal_date' => '2017-04-23', 'amount' => '1000'),
array('deal' => '4', 'deal_date' => '2017-06-02', 'amount' => '2400'),
array('deal' => '5', 'deal_date' => '2017-07-05', 'amount' => '10500'),
);
I am searching for a subset of exactly N elements where the sum of the 'amount' properties is greater then or equal to X and the elements have the lowest 'deal_date' property possible.
If there is no subset that fits the rules:
$subset = false;
So for N=2 and X=10000, I get this output:
$subset = array(
array('deal' => '2', 'deal_date' => '2017-04-17', 'amount' => '8900'),
array('deal' => '4', 'deal_date' => '2017-06-02', 'amount' => '2400'),
);
for N=3 and X=12000:
$subset = array(
array('deal' => '2', 'deal_date' => '2017-04-17', 'amount' => '8900'),
array('deal' => '3', 'deal_date' => '2017-04-23', 'amount' => '1000'),
array('deal' => '4', 'deal_date' => '2017-06-02', 'amount' => '2400'),
);
My current idea entails creating an array that contains arrays of the list of deals in every conceivable order. Then I scan through those for my list of deals that fit the criteria but then I have a list of deals and I am unsure how to determine the 'earliest'.
I'm also looking for the algorithm with the lowest time complexity.
Any ideas would be appreciated.
It is not simple but roughly this
There will be a better way.
The approximate source is
$calculate = 0;
$end_case = array();
$sum = 10000;
// first amount 10000 over value array remove
foreach($deals as $key => $val){
if($val['amount']>=$sum)unset($deals[$key]);
}
// second Obtaining data
foreach($deals as $key => $val){
foreach($deals as $k => $v){
// same deal excetpion
if($val['deal']==$v['deal']) continue;
$calc = deal_sum($val['amount'],$v['amount']);
if($calc>=$sum){
echo "#".$v['deal']." => ".$val['amount']."+".$v['amount']." = ".$calc."\n";
array_push($end_case,$v['deal']);
}
print_r($end_case);
}
}
function deal_sum($source,$destination){
$result = $source+$destination;
return $result;
}
You should keep in mind the time complexity of your algorithm, otherwise large input sets will take forever.
Algorithm for N:
Returns array() of the first $number_of_deals_to_combine deals from
$deals ordered by 'deal_date', that have at least $threshold or
greater combined amounts or false if no such combination is found.
Uses a rolling window of $number_of_deals_to_combine elements
excluding deals that have too small 'amount'.
function combine_deals($deals, $threshold, $number_of_deals_to_combine){
$return = false;
//are there enough deals to combine?
if(count($deals) >= $number_of_deals_to_combine){
//are deals sorted by date? sort them
usort($deals,function($a, $b){return strnatcmp($a['deal_date'],$b['deal_date']);});
//find out if there is a possible solution, by adding up the biggest deals
$amounts = array_map('intval',array_column($deals,'amount'));
rsort($amounts); //sort descending
if(array_sum(array_slice($amounts, 0, $number_of_deals_to_combine)) >= $threshold){
//find the smallest possible number that is part of a solution
$min_limit = $threshold - array_sum(array_slice($amounts, 0, $number_of_deals_to_combine - 1));
//rolling window
$combined = array();
foreach($deals as $deal){
if(intval($deal['amount']) < $min_limit){continue;} //this deal is not part of any solution
$combined[] = $deal;
//keep the number of deals constant
if(count($combined) > $number_of_deals_to_combine){
$combined = array_slice($combined, count($combined) - $number_of_deals_to_combine);
}
//there are enough deals to test
if(count($combined) == $number_of_deals_to_combine){
$amount = array_sum(array_map('intval',array_column($combined, 'amount')));
if($amount >= $threshold){
$return = $combined;
break;
}
}
}
}
}
return $return;
}
$threshold = 10000;
$number_of_deals_to_combine = 2;
$result = combine_deals($deals, $threshold, $number_of_deals_to_combine);
Are the amount fields always integers? If not, replace all intval with floatval everywhere.
So I'm trying to make an array for stat growths in a fake rpg. It looks like this.
// base array
// $base: starting base stats
// $growth: growth rate per rng
$growths = array(
'HP' => array (70 => 20),
'STR' => array (50 => 7),
'MAG' => array (35 => 2),
'SKL' => array (45 => 6),
'SPD' => array (50 => 8),
'LCK' => array (55 => 5),
'DEF' => array (45 => 6),
'RES' => array (15 => 4),
);
//rng calculator
for ($x = 0; $x <= 20; $x++) {
foreach ($growths as $stat_name => $info) {
$roll = rand(0,100);
foreach ($info as $growth => $base) {
if ($roll <= $growth) {
$info[$growth] = ++$base;
print "(UP!) ";
}
echo "$stat_name: $base<br/ >";
}
}
}
My only issue is that the new $base value after the rng calculator refuses to store in the original array. Am I doing something wrong, or do I just need to rebuild the array from scratch and try something else? Any help would be appreciated!
In your first foreach loop, you assign the key of $growths to $stat_name and the value to $info. These are temporary variables. If you change them, the original array is not affected.
// This won't work because $info is temporary.
$info[$growth] = ++$base;
Instead, simply refer to the original array:
// Do this instead.
$growths[$stat_name][$growth] = ++$base;
use reference
just use foreach ($growths as $stat_name => &$info) to replace conrresponding line in your code.
How can I get the maximum distance from the array below? I am getting the following output when i try to print_r($data):
Array
(
[0] => Array
(
[distance] => 1.7 km
[time] => 3 mins
[distance_value] => 1720
[time_value] => 192
)
[1] => Array
(
[distance] => 4.2 km
[time] => 10 mins
[distance_value] => 4207
[time_value] => 587
)
)
I want to echo 4.2 km because it is the max distance in my array.
foreach ($delivery as $key => $value) {
if($key==0) {
$mysource = $pickup;
} else {
$mysource = $delivery[$key-1];
}
$data[$key] = $this->GetDrivingDistance($mysource,$value);
if(!empty($data[$key])) {
$dist += max($data[$key]['distance']);
}
}
echo $dist; exit();
print_r($data); exit();
You can make use of built-in functions:
echo max(array_map('floatval',array_column($array, "distance")))." km";
Explanation:
array_column - converts your array to single dimensional
array_map - Apply float operation to the string. This is important since your string comparison compares two numbers wrongly. When float function is applied your km will be removed, so append later on.
Example for string:
11.7 is less than 4.2, because it compares first character and arranges by 1,2,3...
Output:
4.2 km
Note:
This is suitable if all values are in km,if it is in other units, you need a workaround too!
<?php
$array = array
(
array
(
"distance" => "1.7 km",
"time" => "3 mins",
"distance_value" => 1720,
"time_value" => 192,
),
array
(
"distance" => "4.2 km",
"time" => "10 mins",
"distance_value" => 4207,
"time_value" => 587,
)
);
$distance = [];
$i = 0;
foreach ($array as $distance_data) {
$distance[] = (float)$distance_data["distance"];
$i++;
}
$max_distnace = max($distance);
var_dump($max_distnace);
You can first sort the array by distance and get the required one:
$myArray =
array (
array
(
'distance' => '1.7 km',
'time' => '3 mins',
'distance_value' => 1720,
'time_value' => 192,
),
array
(
'distance' => '4.2 km',
'time' => '10 mins',
'distance_value' => 4207,
'time_value' => 587,
)
);
usort($myArray,function($a, $b) {
return $b['distance_value'] - $a['distance_value'];
});
$maxDistance = $myArray[0];
echo $maxDistance['distance'];
// 4.2 km
I get solution from below code..
foreach ($delivery as $key => $value) {
if($key==0){
$mysource = $pickup;
}else{
$mysource = $delivery[$key-1];
}
$data[$key] = $this->GetDrivingDistance($mysource,$value);
if(!empty($data[$key])){
$max = '-9999999 km'; //will hold max val
$found_item = null; //will hold item with max val;
foreach($data as $k=>$v)
{
if($v['distance']>$max)
{
$max = $v['distance'];
$found_item = $v;
}
}
}
}
$dist = $max;
Thamilan's answer is the best, but as you're using CakePHP you can also use Hash::extract() if array_column() isn't available to you (i.e. you're using an older version of PHP) or if you need to use an array with deeper associations thanks to the Hash utility's path syntax:-
echo max(array_map('floatval', Hash::extract($array, 'distance'))) . ' km';
Here Hash::extract() is working like array_column() and converting your array to a single dimensional array.
Sine this questions is tagged with CakePHP I though I'd give my solution using CakePHP Collection class:
use Cake\Collection\Collection;
(new Collection($data))->sortBy('distance_value')->first()['distance'];
Note that CakePHP has a utility function collection that can make this shorter:
collection($data)->sortBy('distance_value')->first()['distance']
I have an array that is composed of information that looks like the following:
['Jay', 'Jay', 'Jay', 'Spiders', 'Dogs', 'Cats', 'John', 'John', 'John', 'Dogs', 'Cows', 'Snakes']
What I'm trying to do is remove duplicate entries but only if they occur right next to each other.
The correct result should look like the following:
['Jay', 'Spiders', 'Dogs', 'Cats', 'John', 'Dogs', 'Cows', 'Snakes']
I'm using PHP but any kind of logic would be able to help me out with this problem.
Here is some code I've tried so far:
$clean_pull = array();
$counter = 0;
$prev_value = NULL;
foreach($pull_list as $value) {
if ($counter == 0) {
$prev_value = $value;
$clean_pull[] = $value;
}
else {
if ($value != $pre_value) {
$pre_value = value;
}
}
echo $value . '<br>';
}
Francis, when I run the following code:
$lastval = end($pull_list);
for ($i=count($pull_list)-2; $i >= 0; $i--){
$thisval = $pull_list[$i];
if ($thisval===$lastval) {
unset($pull_list[$i]);
}
$lastval = $thisval;
}
# optional: reindex the array:
array_splice($pull_list, 0, 0);
var_export($pull_list);
, I get these results:
array ( 0 => 'NJ Lefler', 1 => 'Deadpool', 2 => 'NJ Lefler', 3 => 'Captain Universe: The Hero Who Could Be You', 4 => 'NJ Lefler', 5 => 'The Movement', 6 => 'NJ Lefler', 7 => 'The Dream Merchant', 8 => 'Nolan Lefler', 9 => 'Deadpool', 10 => 'Nolan Lefler', 11 => 'Captain Universe: The Hero Who Could Be You', 12 => 'Nolan Lefler', 13 => 'The Movement', 14 => 'Tom Smith', 15 => 'Deadpool', 16 => 'Tom Smith', 17 => 'Captain Universe: The Hero Who Could Be You', )
Your approach (a $prev_value variable) should work fine and you don't need a counter.
Your use of $counter is why your code doesn't work--the first half of the if statement is always executed because $counter is never incremented; and the second half just compares values. The only thing you need to do is compare the current value with the previous value and include the current value only if it differs (or remove it only if it's the same).
It's much easier to see this algorithm if you use functional reduction. Here is an example using array_reduce:
$a = array('Jay', 'Jay', 'Jay', 'Spiders', 'Dogs', 'Cats', 'John', 'John', 'John', 'Dogs', 'Cows', 'Snakes');
$na = array_reduce($a, function($acc, $item){
if (end($acc)!==$item) {
$acc[] = $item;
}
return $acc;
}, array());
var_export($na);
Note this comparison of var_export($a) (your original array) and var_export($na) (the result produced by the code):
$a = array ( $na = array (
0 => 'Jay', 0 => 'Jay',
1 => 'Jay', 1 => 'Spiders',
2 => 'Jay', 2 => 'Dogs',
3 => 'Spiders', 3 => 'Cats',
4 => 'Dogs', 4 => 'John',
5 => 'Cats', 5 => 'Dogs',
6 => 'John', 6 => 'Cows',
7 => 'John', 7 => 'Snakes',
8 => 'John', )
9 => 'Dogs',
10 => 'Cows',
11 => 'Snakes',
)
The array_reduce() method does exactly the same thing as the following code:
$na = array();
foreach ($a as $item) {
if (end($na)!==$item) {
$na[] = $item;
}
}
Instead of returning a copy of an array, you can also modify the array in-place using the same algorithm but starting from the end of the array:
$lastval = end($a);
for ($i=count($a)-2; $i >= 0; $i--){
$thisval = $a[$i];
if ($thisval===$lastval) {
unset($a[$i]);
}
$lastval = $thisval;
}
# optional: reindex the array:
array_splice($a, 0, 0);
var_export($a);
Keep track of the last element in the array, and skip adding the next element to your new array if you just added it.
Or, you can just check the last element in the array, and see if it's not the current element in your array:
$array = ['Jay', 'Jay', 'Jay', 'Spiders', 'Dogs', 'Cats', 'John', 'John', 'John', 'Dogs', 'Cows', 'Snakes'];
$new = array( array_shift( $array));
foreach( $array as $el) {
if( !($new[count($new) - 1] === $el)) {
$new[] = $el;
}
}
Assuming the array isn't so large that having a second one will cause a problem, the approach you described should work. Does it look like this?
$last = null;
$result = [];
foreach($arr as $item)
if($item !== $last)
$result[] = $last = $item;
Re: edit:
$pre_value and $prev_value aren't the same thing
$counter doesn't change
It looks like you tried to combine a counter approach and a "last" approach somehow.
Define a global variable glob.
pass the array:
if (array[i] == glob) then
remove array[i]
else
glob = array[i];
keep array[i];