PHP compare $_POST array members - php

I have a form where users have to enter timecodes or time values 00:00:00, which at the end must fulfill the condition that each timestamp must be larger than the previous. I know it can be achieved by processing the $_POST['timecode']. However can't make it work. This is what I've been working around. Sorry for my ignorance, but being sure this is the way and not getting the result drove me to ask here.
$q_tc=count($_POST['timecode']);
for ($w = 0; $w < $q_tc; $w++) {
if ($_POST['id_ts'][$w] > $_POST['id_ts'][$w+1]) {
echo "error: should be smaller than next timecode";
} else {
// ...
}
}
What I get after running this code is that everytime an error message is deployed, even without the condition being placed.
I'm running PHP 5.4 on a apache server. This is what the $_POST['timecode'] contains:
Array ( [0] => 00:00:30 [1] => 10:00:00 [2] => 12:00:00 )
And as it can be seen, no error should be generated, but the code does not check accurately the requested condition.

You end up comparing the last one to a null value, because you look at 0 and 1, then 1 and 2, then 2 and 3, but key 3 doesn't exist.
Try this fix. Skip the first one, compare each to the previous.
$arr = array('00:00:30', '10:00:00', '12:00:00');
$q_tc=count($arr);
for ($w = 1; $w < $q_tc; $w++) {
if ($arr[$w-1] > $arr[$w]) {
echo "error: should be smaller than next timecode";
}
}

Related

PHP program to accept user input and terminate when entered zero

I came across this program on http://www.programmr.com . And the question is
Complete the program to print the sum of negative numbers, positive even numbers, positive odd numbers from a list of numbers entered by the user. The list terminates when the number entered is zero . And my code is,
$nums = array();
while(trim(fgets(STDIN)) != 0){
array_push($nums,trim(fgets(STDIN)));
}
I know the code is incomplete but what im trying to do is to push the inputs to the array and then calculate the sum. When I print_r($nums) the array it gives me this,
Array
(
[0] => 34
[1] => 32
[2] => 45
[3] => 0
)
And my input is,
12
34
12
32
12
45
12
0
0
It pushes the alternative elements i dont know whats happening with this. Please help me , thanks in advance.
You are calling fgets(STDIN) twice in your code, i have adjusted it a bit so the array part is working. The rest of the assignment i let you figure that part out ;) Hint: Use modulus operator.
$nums = array();
do {
$number = (int) trim(fgets(STDIN));
array_push($nums,$number);
} while ($number !== 0);
print_r($nums);
Also if you are using PHP5.6 or higher you can use short array syntax like so:
$nums = [];
And
$nums[] = $number;

Create new elements in an array based on existing values

I have the following array which contains a set of periods:
Array
(
Array
(
[period_start] => 1
[period_end] => 12
)
Array
(
[period_start] => 4
[period_end] => 8
)
)
I want to split the periods that overlap other periods. For example, because the second period is overlapping the first, it should split the first period into two periods so that it looks something like this:
Array
(
Array
(
[period_start] => 1
[period_end] => 3
)
Array
(
[period_start] => 4
[period_end] => 8
)
Array
(
[period_start] => 9
[period_end] => 12
)
)
So that no two periods contain a start and end value within the range of another period. But I have no idea how best to achieve this in an efficient manner. Any help would be appreciated.
EDIT: To the comments, this post was more a plea for rubber ducking, not for getting someone else to do my work for me. I've got a solution to my problem (brace yourselves):
// Sort the collection by period_start in ascending order.
function sortByPeriod(&$collection) {
usort($collection, function ($value1, $value2) {
if (!array_key_exists('period_start', $value1) || !array_key_exists('period_start', $value2)) {
return 0;
}
if ($value1['period_start'] == $value2['period_start']) {
return 0;
}
return $value1['period_start'] < $value2['period_start'] ? -1 : 1;
});
}
$periods = array();
$products = array(
array(
'period_start' => 4,
'period_end' => 8
),
array(
'period_start' => 1,
'period_end' => 12
)
);
sortByPeriod($products);
foreach ($products as $product) {
// Store them in $periods using a key, so that if an identical period comes along on a future iteration, it doesn't get counted. The keys aren't required.
if (array_key_exists('period_start', $product) && !is_null($product['period_start'])) {
if (!array_key_exists($product['period_start'] . '-' . $product['period_end'], $periods)) {
$productStart = $product['period_start'];
$productEnd = $product['period_end'];
// Go through each period already inserted
foreach ($periods as &$period) {
$periodStart = $period['period_start'];
$periodEnd = $period['period_end'];
If the product's start overlaps the period's end
if ($productStart <= $periodEnd) {
// Set that period's end to the product's start - 1
$period['period_end'] = $productStart - 1;
// If the overlapping product is entirely within the period (e.g. period is 1-12, product is 4-8, like the example provided earlier)
if ($productEnd <= $periodEnd) {
// Add a new period, whose start is the product's end + 1 and the end is the initial period's end.
$periods[($productEnd + 1) . '-' . $periodEnd] = array(
'period_start' => $productEnd + 1,
'period_end' => $periodEnd
);
// The product's period isn't entirely within the period (e.g. period is 1-6, product is 4-8)
} else {
// Add a new period from product start to period end (e.g. following the example above, the period becomes 1-3, insert 4-6)
$periods[$productStart . '-' . $periodEnd] = array(
'period_start' => $productStart,
'period_end' => $periodEnd
);
// Set the product's start to the period's end + 1 (e.g. 7)
$productStart = $periodEnd + 1;
}
}
}
// Add the period (following the example iteration above, product start = 7, end = 8)
$periods[$productStart . '-' . $productEnd] = array(
'period_start' => $productStart,
'period_end' => $productEnd
);
}
}
// After one iteration, we have 1-3, 4-6 and 7-8
}
sortByPeriod($periods);
$periods = array_values($periods);
print_r($periods);
Which works and yields the expected output as shown above. However, as you can see, it isn't very well organised and I feel as if there would be a better way to approach this.
Thank you.
I realised that I was going about this in a bit of a strange way. My thinking was that if I had a period range (let's say 1-8) and another (4-12), it should split these ranges up and leave it with 1-3, 4-8, 9-12. While the output is indeed the output that I desire, getting to it by splitting it up and adding new periods to compensate for the missing ranges in between was too complicated. I was thinking of it like this (and I had it working this way):
I have three loaves of bread, each a different size. I need to split them all up so that I have several slices of the loaf, enough to equal the content of the largest loaf. Okay, let's take a piece out of this loaf, a piece out of that loaf, and put it all together.
It's all a mess. Really, the best way to go about it is to use the largest loaf and cut in the exact places that the other loaves would fit, so instead of using three loaves, i'm just using one.
I put the theory into practice (using JavaScript. I can port it to PHP). First I need a way for each period to be indistinguishable, so that if my initial periods are something along the lines of 1-3, 1-12 and 4-8, it will count the 1-3 and 4-8 as two separate periods. Luckily, all of my items are products that have a normal price and a discounted price. The discounted price takes effect during the discount period.
Next, I need to identify the largest period and note it's start and end:
var range = {};
this.products.forEach(product => {
if (!range.start) {
range.start = product.start;
}
if (!range.end) {
range.end = product.end;
}
if (product.start < range.start) {
range.start = product.start
}
if (product.end > range.end) {
range.end = product.end;
}
});
Now, for each product, I iterate from start to end by 1 each iteration, storing the product's discount price if it's period is within the current iteration, or otherwise it's normal price:
var periodCounter = [];
this.products.forEach(product => {
for (i = range.start; i <= range.end; i ++) {
if (!periodCounter[i]) {
periodCounter[i] = 0;
}
if (i >= product.start && i <= product.end) {
periodCounter[i] += product.def_amount;
} else {
periodCounter[i] += product.amount;
}
}
});
Great. Now I have an array filled with prices for all products during each single one month period. Now I need to identify the actual periods. This is really easy - iterate through the array, check to see if the current price is equal to the previous price. If it is, we're still in a period. If it isn't, we've reached the end of that period and started a new one:
var periods = [];
var periodStart = 0;
var periodEnd = 0;
for (i = range.start; i <= range.end; i ++) {
if (i == range.start) {
periodStart = i;
} else {
if (periodCounter[i] != periodCounter[i-1]) {
periodEnd = i-1;
periods.push({
start: periodStart,
end: periodEnd,
amount: periodCounter[i-1]
});
periodStart = i;
}
if (i == range.end) {
periods.push({
start: periodStart,
end: i,
amount: periodCounter[i]
});
}
}
}
If by any chance the totals of two "logical" periods equal the same price, it doesn't really matter. The end user just needs to know what the price is for these periods, they gain no information in knowing that two periods equal the same price, so in essence, you might as well just concatenate those periods into one large period, which is what would happen. If it's absolutely necessary to display the true "logical" periods, instead of storing the price in the counter, use a byte value (1, 2, 4, 8, etc).
I made a codepen for this project, using Vue.js and Bulma to display the products and the correct periods. Of course, I'm open to better ways that I might be able to go about this.

Creating hour statistics using arrays and memcached

I am trying to count how many hits I have on my site each hour, but am not sure how to approach this.
Here is what i have now:
if($cacheAvailable == true){ // WE GOT A CACHE
date_default_timezone_set("UTC");
$thisHour = date("H", time());
$moveStats = $memcache->get('moveStats');
if(!$moveStats){
$todayStats = array(array(hour => $thisHour, hits => 1, executetime => $total_time));
$memcache->set('moveStats', $todayStats);
}
foreach ($moveStats as $k => $v) {
if($v['hour'] == $thisHour){
$moveStats[$k]['hits']=$moveStats[$k]['hits']+1;
}
}
$memcache->set('moveStats', $moveStats);
echo '<pre>';
print_r($moveStats);
echo '</pre>';
}
This makes an array like this:
Array
(
[0] => Array
(
[hour] => 18
[hits] => 6
[executetime] => 0
)
)
//##### EDIT ######//
I am able to add to the current hour but I don't know how to add a new hour when the clock turns into the new hour?
Hoping for help and thanks in advance.
You just have to check if that index already exists, if not create a new one, and always increase the old value:
$todayStats = $moveStats;
if (!isset($todayStats [$thisHour])) {
$todayStats[$thisHour] = 0;
}
$todayStats[$thisHour]['hits']++;
$todayStats[$thisHour]['executetime'] = $total_time;
But you have some other problems in your implementation:
- Don't use string without quotes. That will try to call a constant with that name and only as fallback return the string itself. It also raises a notice.
- $thisHour won't contain the current hour. If you really want to have the hour try: date('H') only.

I'm creating a random array in PHP and my code doesnt seem to output a truly random answer

I want to construct an array of 3 offers that output in a random order. I have the following code and whilst it does output 3 random offers it doesn't appear to be random. The first value in the generated array always seems to be from the 1st 2 records in my offers table. The offers table only has 5 records in it (I dont know if this is affecting things).
$arrayOfferCount = $offerCount-1;
$displayThisManyOffers = 3;
$range = range(0, $arrayOfferCount);
$vals = array_rand($range, $displayThisManyOffers);`
Any help or advice would be appreciated.
Working fine here. Benchmark it over lots of runs instead of just gut feeling... here it is for 1,000 tries:
<?php
$offerCount = 5;
$arrayOfferCount = $offerCount-1;
$displayThisManyOffers = 3;
$range = range(0, $arrayOfferCount);
for($i = 0; $i < 1000; $i++) {
$vals = array_rand($range, $displayThisManyOffers);
foreach($vals as $val) {
$counts[$val]++;
}
}
sort($counts);
print_r($counts);
Generates:
Array
(
[0] => 583
[1] => 591
[2] => 591
[3] => 610
[4] => 625
)
I know that mt_rand() is much better PRNG.
However, in your case you need to let the database select them for you
SELECT * FROM ads ORDER BY RAND() LIMIT 0, 3
It is probably randomly picking which to display, but displaying them in the same order they appear in your array. If you do it enough times (~20) you should get the third one to show up once if this is the case (chances of choosing exactly the last 3 out of 5 would be 1 in 5*4, so around every 20th one you'll see the third option appear).
array_rand seems not to work properly sometimes (see PHP-Manual comments).
Workaround: Get the array size and pick a random index using the function mt_rand

PHP: find two or more numbers from a list of numbers that add up towards a given amount

I am trying to create a little php script that can make my life a bit easier.
Basically, I am going to have 21 text fields on a page where I am going to input 20 different numbers. In the last field I will enter a number let's call it the TOTAL AMOUNT. All I want the script to do is to point out which numbers from the 20 fields added up will come up to TOTAL AMOUNT.
Example:
field1 = 25.23
field2 = 34.45
field3 = 56.67
field4 = 63.54
field5 = 87.54
....
field20 = 4.2
Total Amount = 81.90
Output: field1 + fields3 = 81.90
Some of the fields might have 0 as value because sometimes I only need to enter 5-15 fields and the maximum will be 20.
If someone can help me out with the php code for this, will be greatly appreciated.
If you look at oezis algorithm one drawback is immediately clear: It spends very much time summing up numbers which are already known not to work. (For example if 1 + 2 is already too big, it doesn't make any sense to try 1 + 2 + 3, 1 + 2 + 3 + 4, 1 + 2 + 3 + 4 + 5, ..., too.)
Thus I have written an improved version. It does not use bit magic, it makes everything manual. A drawback is, that it requires the input values to be sorted (use rsort). But that shouldn't be a big problem ;)
function array_sum_parts($vals, $sum){
$solutions = array();
$pos = array(0 => count($vals) - 1);
$lastPosIndex = 0;
$currentPos = $pos[0];
$currentSum = 0;
while (true) {
$currentSum += $vals[$currentPos];
if ($currentSum < $sum && $currentPos != 0) {
$pos[++$lastPosIndex] = --$currentPos;
} else {
if ($currentSum == $sum) {
$solutions[] = array_slice($pos, 0, $lastPosIndex + 1);
}
if ($lastPosIndex == 0) {
break;
}
$currentSum -= $vals[$currentPos] + $vals[1 + $currentPos = --$pos[--$lastPosIndex]];
}
}
return $solutions;
}
A modified version of oezis testing program (see end) outputs:
possibilities: 540
took: 3.0897309780121
So it took only 3.1 seconds to execute, whereas oezis code executed 65 seconds on my machine (yes, my machine is very slow). That's more than 20 times faster!
Furthermore you may notice, that my code found 540 instead of 338 possibilities. This is because I adjusted the testing program to use integers instead of floats. Direct floating point comparison is rarely the right thing to do, this is a great example why: You sometimes get 59.959999999999 instead of 59.96 and thus the match will not be counted. So, if I run oezis code with integers it finds 540 possibilities, too ;)
Testing program:
// Inputs
$n = array();
$n[0] = 6.56;
$n[1] = 8.99;
$n[2] = 1.45;
$n[3] = 4.83;
$n[4] = 8.16;
$n[5] = 2.53;
$n[6] = 0.28;
$n[7] = 9.37;
$n[8] = 0.34;
$n[9] = 5.82;
$n[10] = 8.24;
$n[11] = 4.35;
$n[12] = 9.67;
$n[13] = 1.69;
$n[14] = 5.64;
$n[15] = 0.27;
$n[16] = 2.73;
$n[17] = 1.63;
$n[18] = 4.07;
$n[19] = 9.04;
$n[20] = 6.32;
// Convert to Integers
foreach ($n as &$num) {
$num *= 100;
}
$sum = 57.96 * 100;
// Sort from High to Low
rsort($n);
// Measure time
$start = microtime(true);
echo 'possibilities: ', count($result = array_sum_parts($n, $sum)), '<br />';
echo 'took: ', microtime(true) - $start;
// Check that the result is correct
foreach ($result as $element) {
$s = 0;
foreach ($element as $i) {
$s += $n[$i];
}
if ($s != $sum) echo '<br />FAIL!';
}
var_dump($result);
sorry for adding a new answer, but this is a complete new solution to solve all problems of life, universe and everything...:
function array_sum_parts($n,$t,$all=false){
$count_n = count($n); // how much fields are in that array?
$count = pow(2,$count_n); // we need to do 2^fields calculations to test all possibilities
# now i want to look at every number from 1 to $count, where the number is representing
# the array and add up all array-elements which are at positions where my actual number
# has a 1-bit
# EXAMPLE:
# $i = 1 in binary mode 1 = 01 i'll use ony the first array-element
# $i = 10 in binary mode 10 = 1010 ill use the secont and the fourth array-element
# and so on... the number of 1-bits is the amount of numbers used in that try
for($i=1;$i<=$count;$i++){ // start calculating all possibilities
$total=0; // sum of this try
$anzahl=0; // counter for 1-bits in this try
$k = $i; // store $i to another variable which can be changed during the loop
for($j=0;$j<$count_n;$j++){ // loop trough array-elemnts
$total+=($k%2)*$n[$j]; // add up if the corresponding bit of $i is 1
$anzahl+=($k%2); // add up the number of 1-bits
$k=$k>>1; //bit-shift to the left for looking at the next bit in the next loop
}
if($total==$t){
$loesung[$i] = $anzahl; // if sum of this try is the sum we are looking for, save this to an array (whith the number of 1-bits for sorting)
if(!$all){
break; // if we're not looking for all solutions, make a break because the first one was found
}
}
}
asort($loesung); // sort all solutions by the amount of numbers used
// formating the solutions to getting back the original array-keys (which shoud be the return-value)
foreach($loesung as $val=>$anzahl){
$bit = strrev(decbin($val));
$total=0;
$ret_this = array();
for($j=0;$j<=strlen($bit);$j++){
if($bit[$j]=='1'){
$ret_this[] = $j;
}
}
$ret[]=$ret_this;
}
return $ret;
}
// Inputs
$n[0]=6.56;
$n[1]=8.99;
$n[2]=1.45;
$n[3]=4.83;
$n[4]=8.16;
$n[5]=2.53;
$n[6]=0.28;
$n[7]=9.37;
$n[8]=0.34;
$n[9]=5.82;
$n[10]=8.24;
$n[11]=4.35;
$n[12]=9.67;
$n[13]=1.69;
$n[14]=5.64;
$n[15]=0.27;
$n[16]=2.73;
$n[17]=1.63;
$n[18]=4.07;
$n[19]=9.04;
$n[20]=6.32;
// Output
$t=57.96;
var_dump(array_sum_parts($n,$t)); //returns one possible solution (fuc*** fast)
var_dump(array_sum_parts($n,$t,true)); // returns all possible solution (relatively fast when you think of all the needet calculations)
if you don't use the third parameter, it returns the best (whith the least amount numbers used) solution as array (whith keys of the input-array) - if you set the third parameter to true, ALL solutions are returned (for testing, i used the same numbers as zaf in his post - there are 338 solutions in this case, found in ~10sec on my machine).
EDIT:
if you get all, you get the results ordered by which is "best" - whithout this, you only get the first found solution (which isn't necessarily the best).
EDIT2:
to forfil the desire of some explanation, i commented the essential parts of the code . if anyone needs more explanation, please ask
1. Check and eliminate fields values more than 21st field
2. Check highest of the remaining, Add smallest,
3. if its greater than 21st eliminate highest (iterate this process)
4. If lower: Highest + second Lowest, if equal show result.
5. if higher go to step 7
6. if lower go to step 4
7. if its lower than add second lowest, go to step 3.
8. if its equal show result
This is efficient and will take less execution time.
Following method will give you an answer... almost all of the time. Increase the iterations variable to your taste.
<?php
// Inputs
$n[1]=8.99;
$n[2]=1.45;
$n[3]=4.83;
$n[4]=8.16;
$n[5]=2.53;
$n[6]=0.28;
$n[7]=9.37;
$n[8]=0.34;
$n[9]=5.82;
$n[10]=8.24;
$n[11]=4.35;
$n[12]=9.67;
$n[13]=1.69;
$n[14]=5.64;
$n[15]=0.27;
$n[16]=2.73;
$n[17]=1.63;
$n[18]=4.07;
$n[19]=9.04;
$n[20]=6.32;
// Output
$t=57.96;
// Let's try to do this a million times randomly
// Relax, thats less than a blink
$iterations=1000000;
while($iterations-->0){
$z=array_rand($n, mt_rand(2,20));
$total=0;
foreach($z as $x) $total+=$n[$x];
if($total==$t)break;
}
// If we did less than a million times we have an answer
if($iterations>0){
$total=0;
foreach($z as $x){
$total+=$n[$x];
print("[$x] + ". $n[$x] . " = $total<br/>");
}
}
?>
One solution:
[1] + 8.99 = 8.99
[4] + 8.16 = 17.15
[5] + 2.53 = 19.68
[6] + 0.28 = 19.96
[8] + 0.34 = 20.3
[10] + 8.24 = 28.54
[11] + 4.35 = 32.89
[13] + 1.69 = 34.58
[14] + 5.64 = 40.22
[15] + 0.27 = 40.49
[16] + 2.73 = 43.22
[17] + 1.63 = 44.85
[18] + 4.07 = 48.92
[19] + 9.04 = 57.96
A probably inefficient but simple solution with backtracking
function subset_sums($a, $val, $i = 0) {
$r = array();
while($i < count($a)) {
$v = $a[$i];
if($v == $val)
$r[] = $v;
if($v < $val)
foreach(subset_sums($a, $val - $v, $i + 1) as $s)
$r[] = "$v $s";
$i++;
}
return $r;
}
example
$ns = array(1, 2, 6, 7, 11, 5, 8, 9, 3);
print_r(subset_sums($ns, 11));
result
Array
(
[0] => 1 2 5 3
[1] => 1 2 8
[2] => 1 7 3
[3] => 2 6 3
[4] => 2 9
[5] => 6 5
[6] => 11
[7] => 8 3
)
i don't think the answer isn't as easy as nik mentioned. let's ay you have the following numbers:
1 2 3 6 8
looking for an amount of 10
niks solution would do this (if i understand it right):
1*8 = 9 = too low
adding next lowest (2) = 11 = too high
now he would delete the high number and start again taking the new highest
1*6 = 7 = too low
adding next lowest (2) = 9 = too low
adding next lowest (3) = 12 = too high
... and so on, where the perfect answer would simply
be 8+2 = 10... i think the only solution is trying every possible combination of
numbers and stop if the amaunt you are looking for is found (or realy calculate all, if there are different solutions and save which one has used least numbers).
EDIT: realy calculating all possible combiations of 21 numbers will end up in realy, realy, realy much calculations - so there must be any "intelligent" solution for adding numbers in a special order (lik that one in niks post - with some improvements, maybe that will bring us to a reliable solution)
Without knowing if this is a homework assignment or not, I can give you some pseudo code as a hint for a possible solution, note the solution is not very efficient, more of a demonstration.
Hint:
Compare each field value to all field value and at each iteration check if their sum is equal to TOTAL_AMOUNT.
Pseudo code:
for i through field 1-20
for j through field 1-20
if value of i + value of j == total_amount
return i and j
Update:
What you seem to be having is the Subset sum problem, given within the Wiki link is pseudo code for the algorithm which might help point you in the right direction.

Categories