Array range with exceptions - php

In the PHP function range there are a start point, a end point and a step point.
is it possible to create an array with numbers, in which some values should not exist?
$hundred_tens = range(120, 190, 10);
I need the numbers 220,230,...290 ... 920,930..990, but not 200,210 ...900,910.
My solution:
$hundreds = range(100, 800, 100);
foreach ($hundred_tens as $value) {
$add_numbers[] = $value + $hundreds[0];
$add_numbers[] = $value + $hundreds[1];
$add_numbers[] = $value + $hundreds[2];
$add_numbers[] = $value + $hundreds[3];
$add_numbers[] = $value + $hundreds[4];
$add_numbers[] = $value + $hundreds[5];
$add_numbers[] = $value + $hundreds[6];
$add_numbers[] = $value + $hundreds[7];
}
$all_hundred_tens = array_merge($hundred_tens, $add_numbers);
I have add in a foreach every array value $hundreds to in a new array and merge this array with $hundred_tens.

You can either loop through your entire range and exclude the unnecessary numbers
$hundred_tens = array();
foreach (range(100, 800, 10) as $number) {
if ($number % 100 !== 0 && $number % 100 !== 10) {
$hundred_tens[] = $number;
}
}
or you can merge smaller ranges:
$hundred_tens = array_merge(range(120, 190, 10), range(220, 890, 10), range(920, 990, 10));
Which one is better depends on how many exclusions you have, and if they are together in the list.
Ref my comment below about for ( being faster, here's an example (only one line is different)
$hundred_tens = array();
for ($number = 100; $number <= 1000; $number += 10) {
if ($number % 100 !== 0 && $number % 100 !== 10) {
$hundred_tens[] = $number;
}
}

Basically, you create a for-loop starting from 220 till 910 in steps of 10. In the loop you check whether the number is a hundred or a hundred plus ten. If it is not, then add the number to the array.
To check whether a number is divisable by 100, you could use the module operator %.
So, a % 100 == 0 will result in true, if a is divisable by 100 and false otherwise. For hundreds plus ten, you could do: (a - 10) % 100 == 0.

Related

Format number to N significant digits in PHP

I would like to format (round) float (double) numbers to lets say 2 significant digits for example like this:
1 => 1
11 => 11
111 => 110
119 => 120
0.11 => 0.11
0.00011 => 0.00011
0.000111 => 0.00011
So the arbitrary precision remains same
I expect there is some nice function for it already built in, but could not find any so far
I was pointed to How to round down to the nearest significant figure in php, which is close but doesn't work for N significant digits and I'm not sure what it does with 0.000XXX numbers
To get a number rounded to n significant figures you need to find the size of the number in powers of ten, and subtract that from n.
This works fine for simple rounding:
function sigFig($value, $digits)
{
if ($value == 0) {
$decimalPlaces = $digits - 1;
} elseif ($value < 0) {
$decimalPlaces = $digits - floor(log10($value * -1)) - 1;
} else {
$decimalPlaces = $digits - floor(log10($value)) - 1;
}
$answer = round($value, $decimalPlaces);
return $answer;
}
This will give the following:
0.0001234567 returns 0.0001235
123456.7 returns 123500
However a value such as 10 to four significant figures should strictly be represented as 10.00 to signify the precision to which the value is known.
If this is the desired output you can use the following:
function sigFig($value, $digits)
{
if ($value == 0) {
$decimalPlaces = $digits - 1;
} elseif ($value < 0) {
$decimalPlaces = $digits - floor(log10($value * -1)) - 1;
} else {
$decimalPlaces = $digits - floor(log10($value)) - 1;
}
$answer = ($decimalPlaces > 0) ?
number_format($value, $decimalPlaces) : round($value, $decimalPlaces);
return $answer;
}
Now 1 is displayed as 1.000
With little modification to possible duplicate, answer by Todd Chaffee:
public static function roundRate($rate, $digits)
{
$mod = pow(10, intval(round(log10($rate))));
$mod = $mod / pow(10, $digits);
$answer = ((int)($rate / $mod)) * $mod;
return $answer;
}
To make sigFig(0.9995, 3) output 1.00, use
if(floor(log10($value)) !== floor(log10(round($value, $decimalPlaces)))) {$decimalPlaces--;}
Said line of code should be placed before declaring $answer.
If input $value is negative, set a flag and remove the sign at the beginning of the function, like this:
if($value < 0){$flag = 1;}
$value = ltrim($value, "-");
Then right before returning $answer, detect if the flag is set and if so restore the negative sign, like this:
if(isset($flag)){$answer = "-".$answer;}
Finally, for result values with ambiguous number of significant digits (e.g., 1000, 12000,...), express the result in scientific notation to the desired number of significant digits using sprintf or printf.

How to compress an array?

Lets say, I have an array with 1000 values (integers). And I need from this array to have an array with f.e. 400 values (the number can be changed, f.e. 150, etc.).
So I need to return each 2.5th array value, i.e. 1st, 3rd, 6th, 8th, 11th, etc.
Is this somehow possible?
I dont need a code, I just need some way, how to do it.
EDITED:
My array is array of elevations (from another array with GPS coordinates). And I want to draw a 2D model of elevation. Lets say, my map will always have 400px width. 1px = 1 point of elevation. Thats why I need each 2.5th elevation value...
Like this:
You don't want the 2.5th one. You effectively want to divide the set into blocks of five and get the first and third from each set.
0 1 2 3 4
5 6 7 8 9
10 11 12 13 14
You want the first and third columns.
This is PHP, so we've got 0-based arrays.
We can divide it into groups of 5 using the modulo operator %. We can then see if the return value is 0 (i.e. it's in the first column) or 2 (i.e. it's in the third column).
I'm going to presume your array has numeric keys starting from 0.
// PHP 5.6
$filtered = array_filter($array, function($value, $key) {
$mod = $key % 5;
return ($mod === 0) || ($mod === 2);
}, ARRAY_FILTER_USE_BOTH);
// pre-PHP 5.6
$filtered = array();
foreach ($array as $key => $value) {
$mod = $key % 5;
if (($mod === 0) || ($mod === 2)) {
$filtered[$key] = $value;
}
}
var_dump($filtered);
How about using the modulo % operator?
Say you want to make 1000 into 200 values, loop through all the items in the array and keep a counter, if the counter % 5 == 0 then put that value into a new array, or if != 0 then remove from array. We use modulo 5 because 1000 / 200 = 5.
Below is the way to start with. It does not ensure that first and last elements are included in the output and, probably, has some other glitches. But since you requested the idea, here you go—array_reduce:
$a=[1,2,3,4,5,6,7,8,9,10,11,12]
$step = 2.5;
$i = 0;
$r = array_reduce($a, function($memo, $curr) use(&$i, $step) {
if($i === round($step * count($memo))) {
$memo[] = $curr;
}
$i++;
return $memo;
}, []);
print_r($r);
/*
Array
(
[0] => 1
[1] => 4
[2] => 6
[3] => 9
[4] => 11
)
*/
Hope it helps.
This should be pretty close to what you seem to be looking for. It will collect whichever values are closest (rounding down) to the float values.
$list = range(1,1000);
$targetSize = 300;
$new = array();
$step = count($list) / $targetSize;
$curStep = 0;
for( $i = 0; $i < count($list); $i++ ) {
$curStep++;
if( $curStep > $step ) {
$new[] = $list[ floor($i) ];
$curStep -= $step;
}
}
So this is it:
$arr = range(1, 1387); // f.e.
$cnt = 296; // f.e.
$new = array();
$max = count($arr);
$step = $max / $cnt;
for ($i = 0; $i < $max; $i += $step) {
$new[] = round($arr[(int)$i]);
}

Generate an array in PHP of random number not close to the X previous element

I want to generate in PHP an array of random numbers, but each number should not be the same as any of the X (for example 2 ) numbers bofore it and not even close to any of them by a define range (for example 5).
So for example:
I need numbers between 1 and 100
i've set my "range" to 5
the first two generated number are 20 and 50.
the third number will be a random number between 1 and 100, excluding all the numbers between 15 and 25, and between 45 and 55.
I can't figure out a function to achieve it. Ideally I want to call something like this:
getRandomNumbers( $min, $max, $previous, $range);
where $previous is the number of previous elements to take in consideration when generating the next one and $range is the "proximity" to those number where I don't want the next number to be.
I hope I explained in a decent way my request. :) Please, add a comment if you have any question about it.
I just came up with this:
function getRandomNumbers($min, $max, $previous, $range) {
static $generated = array();
$chunk = array_slice($generated, -$previous);
// Added this infinite loop check to save you some headache.
if (((($max - $min + 1) / (($range * 2) + 1)) + 1) <= $previous) {
die("Values set have the potential of running into an infinite loop. Min: $min, Max: $max, Previous: $previous, Range: $range");
}
while(true) {
$number = rand($min, $max);
$found = true;
foreach ($chunk as $value) {
if (in_array($number, range($value-$range, $value+$range))) {
$found = false;
}
}
if ($found) {
$generated[] = $number;
return $number;
}
}
}
Test it using this:
for ($i = 1; $i < 25; $i++) {
echo getRandomNumbers(1, 100, 5, 5) . "<br />";
}
PHPFiddle Link: http://phpfiddle.org/main/code/51ke-4qzs
Edit: Added a check to prevent a possible infinite loop. For example: if you set the following values:
$min = 1;
$max = 100;
$previous = 5;
$range = 12;
echo getRandomNumbers($min, $max, $previous, $range);
Then let's say, in a really unfortunate situation it would generate 13, 38, 63 and 88. So the 5th number cannot be anything between 1 and 25, 26 and 50, 51 and 75, 76 and 100. So it would result in an infinite loop. I've updated the PHPFiddle link as well.
getRandomNumbers( $previous, $range ) {
//I'm assuming that previous will be an array of your previous X that you don't want to be close to
$num = getRandomNumber() //However you are doing this now
foreach( $previous as $key => $value ) {
if ( ( $value - $range ) > $num && ( $value + $range ) < $num ) {
return getRandomNumbers($previous, $range);
}
}
//You need to also replace a value in previous
return num;
}

Round down to nearest multiple of 10000 using PHP

If I have the following code which grabs an array of values and adds them all together, how can I then round them down to the nearest 10000 using PHP?
Here's the code I currently have
$rows = $db->get("sales");
$sales = 0;
foreach($rows as $row) {
$stock = $sales + $row['sales'];
}
return $sales;
An example result would be
146740
How could I then make that returned as
140000
Although if I had a number greater than 1 million, how could I have that returned as just 1 million?
Divide by 10000, use floor to round down to an integer, then multiply by 10000:
$x = 146740;
$x = 10000 * floor($x/10000);
Or subtract the remainer:
$x = 146740;
$x = $x - ($x % 10000);
To extend this to 1 million, you can do:
if ($x > 1000000) {
$divisor = 1000000;
} elseif ($x > 10000) {
$divisor = 10000;
} else {
$divisor = 1;
}
$x = $x - ($x % divisor);
you could divide the value by 1000. If it is integer 146740/1000 = 146. And after that multiply by 1000 will give 146000

Adding and subtracting within a range of numbers and looping round

I'm wondering how I would go about the addition and subtraction of numbers in a set range and which would loop back on themselves, example below;
Range: 1 - 10
So if I now had the number 7 and added 5 to it, I would want the number go to 2
8, 9, 10, loop around to 1, 2.
And the same if I subtracted, so I have the number 3 and I subtract 4 so I should be left with 9.
2, 1, loop around to 10, 9
I hope this makes sense.
Thanks.
You can use % operator.
It calculates remainder after division.
For example:
$d = 10;
$x = 7;
$y = 5;
echo ($x + $y) % $d;
gives 2;
With negative values you can just remove MINUS
Use the modulus operator.
result = (a + b) % 10;
You can use modulo function like (7+5)%10 = 2
Try this:
$range = range(1,10);
$min = min($range);
$max = max($range);
function operate( $a, $b, $operation ) {
global $max, $min;
switch( $operation ) {
case '+':
$a += $b;
break;
case '-':
$a -= $b;
break;
}
if( $a < $min ) {
return $max + $a;
} else if( $a > $max ) {
return $a - $max;
}
}
Hope it helps.
You can do this with code like
$range = array('from' => 3, 'to' => 13);
$dist = $range['to'] - $range['from'];
$a = 7;
$b = 14;
echo ($dist + ($a % $range['to'] - $b % $range['to'])) % $dist; // $a - $b
echo ($dist + ($a % $range['to'] + $b % $range['to'])) % $dist; // $a + $b
Modulo will do the trick as others have shown, but you must also account for the lower end of the range.
E.g. looping an arbitrary value within an hour range will work since it's zero-based. But if you want to loop a value within a month range you will get into trouble with the last day, because:
31 % 31 = 0
So you will loop to zero when you should remain on 31.
To deal with any range, you need to do this:
$min = 5;
$max = 15;
$value = 25; // The range is 11, so we want this turned into 14
$range = $max - $min + 1;
$value = (($value-$min) % $range) + $min;
To deal with values below minimum:
$range = $max - $min + 1;
$value = ($min - $value) % $range;
$value = $max - ($value - 1);

Categories