PHP select a range from an array() - php

I have an array() of years, they are in order lowest to highest, they are not sequential, so 1100, 1295, 1733,1734, 1980 etc. The key starts at 0 up to the number of years. So [0] => 1100, [1] => 1295.. [5] => 1980. What i am trying to do is pull a range out from the middle, so a user inputs start year = 1200 and end year = 1900. Can anyone help me create a new array from my original array with values that are between the start and end years. Thanks for your help. I am using PHP version 5.6+ cant remember the exact version.

I think array_filter with a custom callback is the shortest way to get the time span:
$array = [1100, 1295, 1733, 1734, 1980];
$start = 1200;
$end = 1733;
$result = array_filter($array, function ($value) use ($start, $end)
{return $start <= $value && $value <= $end;});
var_dump($result);
But there are many other ways.

Related

Split 5 or 6 character number before fourth-last number

I get POST request date strings in the format of mY (no leading zeros on the month), so there's no delimiting character to split on.
Examples: 122019 or 62019
I need to separate the five or six digit string into a one or two digit month and a four digit year.
Eg1 : 122019
$a[0] = 12;
$a[1] = 2019
Eg2 : 62021
$a[0] = 6;
$a[1] = 2021
I don't know about this date format, especially about how you use an integer as a date. But let's consider that it's on purpose, the year is always gonna be 4 character, so you can just get the year by taking the last 4 char and use the rest as the month.
Using substr() see PHP.NET Substr
Return part of a string
And you can specify the start and length of the part you want to get, or using - to get character starting from the end of the string.
$weirdDate = 122019;
//takes the last 4 character
$year = substr($weirdDate , -4);
//takes the string from the beginning to 4 char before the end
$month = substr($weirdDate , 0,strlen($weirdDate)-4);
echo $year;
echo $month;
Again, it seems like a weird way to get a month/year date, but i'm answering based on the assumption that a year is gonna be 4 char long.
If it's not, you can't really split the number since the month part can be 1 or 2 char long.
09-2019 would be 92019
11-2019 would be 112019
A simple use of the substr() function will do this nicely
$in = '122019';
$year = substr($in,-4);
$month = substr($in,0, strlen($in)-4);
echo $year . ' month ' . $month;
$a[] = $month;
$a[] = $year;
RESULT
2019 month 12
array (
[0] => 12
[1] => 2019
)
Or if we use $in = '62019';
The RESULT would be
2019 month 6
array (
[0] => 6
[1] => 2019
)
Reference 'substr()`
You could also use substr with strpos. With substr(), you could first get the year by providing a negative offset to start capturing from back of the string as the year is going to be 4 digits. Then, you could use strpos() to find the index of the year and use this as the ending index to get the month.
That being said, best way to deal with this data is to either have a proper date format or better to have a JSON string with proper keys for days, month and year along with date.
Code:
<?php
$str = '122019';
$year = substr($str,-4);
$month = substr($str,0,strpos($str,$year));
echo $month," ",$year;
I have one another solution to use str_replace() with substr() like:
<?php
$string = "122019";
$year = substr($string, -4);
$date = str_replace($year, "", $string);
$myArray = array($date,$year); // convert into an array
echo "<pre>";
print_r($myArray);
?>
Desired Output:
Array
(
[0] => 12
[1] => 2019
)
Side Note: This will only work, if your year based on last 4 characters and other then these 4 characters must be date, as you mentioned in your question.
To accomplish this feat with two function calls, use negative parameters with substr() calls.
There is no reason to call strlen(), strpos(), or str_replace().
For a single call technique, use preg_split() with a lookahead pattern to ensure that no characters are consumed while exploding.
Codes: (Demo)
$mY = '62021';
var_export([
substr($mY, 0, -4), // month
substr($mY, -4) // year
]);
echo "\n---\n";
var_export(
preg_split('/(?=\d{4}$)/', $mY)
);

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;

Identity the available range numbers when two range numbers are overlap

If I have an existing range:
1-10
11-50
Then I will enter a new range from 1 - 60, How could I detect that the new range to be added overlaps to the previous entries? And how can I get the available range? In this case the available range is from 51-60.
Does anyone here have a great idea on this?
Thanks for helping.
Here's my current code:
$saved = array(
array(
"start" => 1,
"end" => 10
),
array(
"start" => 10,
"end" => 50
)
);
$new_range = array(
"start" => 1,
"end" => 60
);
$usable_range = array();
$previous_from = 0;
$previous_to = 0;
foreach($saved as $value)
{
$range_start = 0;
$range_end = 0;
if($previous_from<$value['start'])
{
$previous_from = $value['start'];
}
if($previous_to<$value['end'])
{
$previous_to = $value['end'];
}
if($previous_from<=$new_range['start'])
{
if($previous_to<$new_range['end'])
{
$range_start = $previous_to+1;
$range_end = $new_range['end'];
$new_range['start'] = $range_start;
}
}
else if($previous_from>=$new_range['start'])
{
if($previous_to<$new_range['end'])
{
$range_start = $previous_to+1;
$range_end = $new_range['end'];
$new_range['start'] = $range_start;
}
}
$usable[] = array("range_start"=>$range_start,"range_end"=>$range_end);
}
Call every interval (min,max)
1) Sort your list of intervals by their min.
2) Check to see if any max is greater than the next entry over's min. If they are, create the interval that is the smaller of their mins and the greater of their maxes, and add it back into the list in place of those two.
3) Whenever you get a new interval, binary search into the list to add it, sorted by min. Then, using similar logic as in 2), attempt to merge it with the entry one below and one above until no more merges are possible.
EDIT: A few changes.
First, since we're using integers not floats, if you have an interval like 1,10 and 11,20 then there is no 'gap' between the 10 and 11 - so we should consider this to be one larger interval, 1,20. Reflect this by, instead of checking to see if max > next min, if max >= next min - 1.
Second, if you want to find all intervals formed by overlap when merging a new interval into your list of intervals:
1) Since your list of intervals is known to be in a state where it is sorted by min and also sorted by max, binary search into it to find the lowest min just above your new min and the highest max just above your new max.
2) Iterate over min, max, min, max... etc in your array that are between your new min and new max. Below the lowest min, above the highest max and between each max/min you can compute the interval that is in the 'gap' there, and return them in e.g. an array.
For example if your list of intervals contains 13,16, 21,24 and 31, 38 and you want to calculate the non-overlap of the new interval 1,30:
1) We binary search into our list and find that 13 is the lowest min above 1 and 24 is the highest max above 30.
2) We iterate like so:
Between our min and the lowest min is 1 and 13 - so this forms an interval 1,12 (inclusive bottom, exclusive top). Add onto the return array.
Between max and the next min is 16 and 21 - so this forms an interval 17,20 (exclusive on both ends). Add onto the return array.
Between max and our max is 24 and 30 - so this forms an interval 25,30 (exclusive bottom, inclusive top). Add onto the return array.
Finding overlap can be described as finding an intersection. Likewise finding the available range can be described as finding the difference. One way of doing this would be treating these sets as arrays and using the array_intersect [1] and array_diff [2] functions.
That is all I can come up with given your details.
[1] - http://php.net/manual/en/function.array-intersect.php
[2] - http://www.php.net/manual/en/function.array-diff.php

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