Is there a way to find out how many value-elements are in one array in all nesting levels?
I want to find out how many are in $_REQUEST to give out a warning if the limit dictated by the max_input_vars directive is nearly reached?
I want to count only the real values, so array-elements should not be counted if they are another array (see
http://pastebin.com/QAKxxqJf)
use: count($array_name, COUNT_RECURSIVE);
as count method takes a second argument which is the mode. int COUNT_NORMAL or COUNT_RECURSIVE
Use array_walk_recursive to count all elements that are not of type array or object:
$count = 0;
array_walk_recursive($a, function($v) use(&$count) {
if(!is_object($v)) ++$count; //or if(is_string($v))
});
This is my solution for PHP <= 5.2
/**
* Counts only the real values, so array-elements
* are not counted if they are another array.
*
* #param array $array
* #return int
* #author Muslim Idris
*/
function count_recursive($array) {
$count = 0;
foreach ($array as $v) {
if (is_array($v)) $count += count_recursive($v);
else $count++;
}
return $count;
}
I cannot use count($_REQUEST, COUNT_RECURSIVE), so I used:
function count_recursive($array) {
$count = 0;
foreach ($array as $id => $_array) {
if (is_array ($_array)) $count += count_recursive ($_array);
else $count += 1;
}
return $count;
}
$num=count_recursive($_REQUEST);
$max=ini_get('max_input_vars');
$debug=true;
if($debug and $max - $num < 50) {
echo "Warning:Number of requests ($num) is near the value of max_input_vars:$max";
}
(although there is still a strange thing is on my testing server, but thats another question: why is the number of elements in $_REQUEST smaller than the limit i set by max_input_vars? and maybe a bug in PHP)
check following link for all option of using count function
http://php.net/manual/en/function.count.php
Related
Given this code (https://psalm.dev/r/156e52eb66):
<?php
function keys(): array
{
return ['foo', 'bar'];
}
// no lines above can be changed
foreach (keys() as $k) {
echo gettype($k);
}
how would one type it assuming the keys function is not under our control (in a different project) and it effectively returns an array of mixed (array<array-key, mixed>).
So, one may only change the loop and around it.
Is it even possible?
UPD: I reported https://github.com/vimeo/psalm/issues/2025
If I get it right this might help you:
foreach (array_keys(keys()) as $k) {
echo gettype(keys()[$k])."\n";
}
You could use for loop instead of foreach loop to fix the warning.
$keys = keys();
for( $i = 0; $i < count( $keys); $i++ ) {
echo gettype( $keys[$i] );
}
Here is the link in Psalm https://psalm.dev/r/20c1cbab73
It's a bug of psalm.
Refer to Github: INFO: MixedAssignment - Cannot assign to a mixed type | when using string array key #1281,
And it has been fixed by muglug in this commit 6033345694727d7c3cf84adc76507c3785ed0295
i want to calculate two operations with the help of loop. They are already working and providing result i need. But i want them to look more like coding. So if anybody can help them with the help of for in php
for($i=0;i<something;$i++){
$temp_calc = ;
}
here are two statements.
In first statement length of array is 9.
In second statement length of array is 12.
both statements to be solved in different for loop as they are totally different questions.
$temp_calc = 10*$temp_array[0]+9*$temp_array[1]+8*$temp_array[2]+7*$temp_array[3]+6*$temp_array[4]+5*$temp_array[5]+4*$temp_array[6]+3*$temp_array[7]+2*$temp_array[8];
$temp_calc = 1*$temp_array[0]+3*$temp_array[1]+1*$temp_array[2]+3*$temp_array[3]+1*$temp_array[4]+3*$temp_array[5]+1*$temp_array[6]+3*$temp_array[7]+1*$temp_array[8]+3*$temp_array[9]+1*$temp_array[10]+3*$temp_array[11];
Thanks in advance
It will be a little simpler to use a foreach loop rather than a for loop. If you specifically need to use a for loop because it is a requirement of an assignment, you can check the PHP documentation. There are some examples there of using a for loop to loop over an array. This is a common and basic control structure and it will be more valuable for you to really understand how to use it. The more important part is what goes on inside the loop. There are multiple ways to do this, but here are some basic examples.
First one:
// initialize multiplier and result outside the loop
$multiplier = 10;
$result = 0;
// loop over the values
foreach ($temp_array as $value) {
// add the value * multiplier to the result and decrement the multiplier
$result += $value * $multiplier--;
}
Second one
// initialize multiplier and result outside the loop
$multiplier = 1;
$result = 0;
// loop over the values
foreach ($temp_array as $value) {
// add the value * multiplier to the result
$result += $value * $multiplier;
// switch the multiplier to the alternating value
if ($multiplier == 1) {
$multiplier = 3;
} else {
$multiplier = 1;
}
// The switch can be done more simply using a ternary operator like this:
// $multiplier = $multiplier == 1 ? 3 : 1;
}
for both issues:
$temp_array = array(2,2,2,2,2,2,2,2,2);//sample
function calc_1($temp_array){//first
$total=0;
$count = count($temp_array)+1;
foreach($temp_array as $value){
$total += $count*$value;
$count-=1;
}
return $total;
}
function calc_2($temp_array){//second
$total=0;
foreach($temp_array as $k=>$value){
$total += ($k%2==0) ? 1*$value : 3*$value;//when is even or odd
}
return $total;
}
var_dump(calc_1($temp_array));//resp1
var_dump(calc_2($temp_array));//resp2
If your array is called $myArray, then:
/*Since I can't know what the sequence of the values are that you
are multiplying, and because you might need other sequences in the
future, a function was developed that chooses which sequence you
want to multiply.*/
function findSomeValues($arraySize)
{
switch ($arraySize) {
case 9:
{
$someValues = array(10,9,8,7,4,5,4,3,2);
}
break;
case 12:
{
$someValues = array(1,3,1,3,1,3,1,3,1,3,1,3);
}
break;
default:
$someValues = array();
}
return $someValues;
}
/*This following function then finds how big your array is, looks
for a sequence stored in the findSomeValues function. If a sequence
exist for that array size (in this case if you have an array either
9 or 12 elements long), the result will be calculated and echoed. If
the sequence was not found, an error message would be echoed.*/
function multiplyValues($myArray) {
$result = 0;
$arraySize = count($myArray);//obtaining array size
$someValues = findSomeValues($arraySize);//obtaining sequence to multiply with
if (count($someValues)>0)
{
for($i=0;i<$arraySize;$i++){
$result += $myArray[i]*$someValues[i];
}
echo "result = ".$result."<br>";//result message
}
else
{
echo "you are missing some values<br>";//error message
}
}
Let me know if that worked for you.
Alternative:
If you prefer something a bit simpler:
//this array holds the sequences you have saved:
$sequenceArray = array(
9 => array(10,9,8,7,4,5,4,3,2),
12 => array(1,3,1,3,1,3,1,3,1,3,1,3)
);
//this function does the multiplication:
function multiplyValues($myArray)
{
$arraySize = count($myArray);
for($i=0;i<$arraySize;$i++){
$result += $myArray[i]*$sequenceArray[i];
}
echo "result = ".$result."<br>";//result message
}
For your result
$temp_calc = 10*$temp_array[0]+9*$temp_array[1]+8*$temp_array[2]+7*$temp_array[3]+6*$temp_array[4]+5*$temp_array[5]+4*$temp_array[6]+3*$temp_array[7]+2*$temp_array[8];
You should have for loop as following. It will run the loop till 8th index of your temp_array and multiply each index value with $i and sum up in a variable $temp_calc_1.
<?php
$temp_calc_1 = 0;
for($i=0;$i<9;$i++){
$temp_calc_1 = $temp_calc_1 + ( 10-$i)*$temp_array[$i] ;
}
For your second result
$temp_calc = 1*$temp_array[0]+3*$temp_array[1]+1*$temp_array[2]+3*$temp_array[3]+1*$temp_array[4]+3*$temp_array[5]+1*$temp_array[6]+3*$temp_array[7]+1*$temp_array[8]+3*$temp_array[9]+1*$temp_array[10]+3*$temp_array[11];
The above should be converted to the following loop, this will run loop till your 12th index of temparrayand do the calculation. This time it will multiply each index value of temparray by either 1 and 3. So first time it will multiply with 1 and next time with 3 and so on
//
$temp_calc_2 = 0;
for($i=0;$i<12;$i++){
$j = $i%2?3:1;
$temp_calc_2 = $temp_calc_2 + $j*$temp_array[$i] ;
}
?>
I have a large array of doubles and I need to calculate the 75th and 90th percentile values for the array. What's the most efficient way to do this via a function?
It's been awhile since statistics, so I could be off here - but here's a crack at it.
function get_percentile($percentile, $array) {
sort($array);
$index = ($percentile/100) * count($array);
if (floor($index) == $index) {
$result = ($array[$index-1] + $array[$index])/2;
}
else {
$result = $array[floor($index)];
}
return $result;
}
$scores = array(22.3, 32.4, 12.1, 54.6, 76.8, 87.3, 54.6, 45.5, 87.9);
echo get_percentile(75, $scores);
echo get_percentile(90, $scores);
The answer above could throw an undefined index notice if you use the higher percent value (100) and does not return correct values according to Excel PERCENTILE function. You can see here an example of how it fails.
I've written a function in PHP according the Wikipedia Second varitant, which is the one used in Excel. This function is also protected from a non percentual value (out of range).
function getPercentile($array, $percentile)
{
$percentile = min(100, max(0, $percentile));
$array = array_values($array);
sort($array);
$index = ($percentile / 100) * (count($array) - 1);
$fractionPart = $index - floor($index);
$intPart = floor($index);
$percentile = $array[$intPart];
$percentile += ($fractionPart > 0) ? $fractionPart * ($array[$intPart + 1] - $array[$intPart]) : 0;
return $percentile;
}
Working off of Mark's function above, I believe the function should actually be:
function get_percentile($percentile, $array) {
sort($array);
$index = (($percentile/100) * (count($array))-1;
if (floor($index) == $index) {
return $array[$index];
}
else {
return ($array[floor($index)] + $array[ceiling($index)])/2;
}
}
I think there are three things that needed to be corrected:
Needed to reduce count by one in order to avoid an out-of-range index (mentioned above)
If the calculated index is an integer, then you should be able to just return the index. You only need to average values when the index is not an integer.
For the average, instead of arbitrarily subtracting one from index, it's better to use floor and ceiling to get the indices to average
I have a array with the lvl=>xp correspondance and I would make a function that return the lvl for a specific xp. like $lvl = getLvlOf(15084); //return 5
$lvl_correspondance = array(
1=>100,
2=>520,
3=>2650,
4=>6588,
5=>12061,
6=>23542,
...
n=>xxxxxx
);
I search the easyest and ressourceless way to do it.
Sorry for my poor english :(
Assuming the level values in the array are kept sorted, e.g. (it's 100,200,300, 400, etc... and not 200,500,100,300,400), then a simple scan will do the trick:
$xp = 15084;
$last_key = null;
foreach($lvl_correspondenance as $key => $val) {
if ($val < $xp) {
$last_key = $key;
} else {
break;
}
}
That'll iterate through the array, and jump out as soon as the XP level in the array is larger than the XP level you're looking for, leaving the key of that "last" level in $last_key
function getLvlOf($lvl, $int){
foreach($lvl as $level=>$exp){
if($exp > $int){
return $level-1;
}
}
}
it's O(n) so no magic there...
It looks like your array can be computed live -
XP = exp( 3 * ln(LVL) + 4 ) * 2
You can do the same in reverse, in O(1):
LVL = exp(ln(XP/2) - 4 / 3)
I rounded the equation, so there may be a +/- 1 issue
Good Luck!
Not good if you have very high level values, but:
$lvl = $lvl_correspondance[array_search(
max(array_intersect(
array_values($lvl_correspondance),
range(0,$points)
)
),
$lvl_correspondance
)];
You can use array_flip if the xp levels are distinct. Then you can simply access the level number using the xp as index:
$levels = array_flip($lvl_correspondance);
$lvl = $levels[15084];
EDIT: But maybe a function would be better to fetch even the xp levels in between:
function getLvlOf($xp) {
// get the levels in descending order
$levels = array_reverse($GLOBALS['lvl_correspondance'], true);
foreach ($levels as $key => $value) {
if ($xp >= $value)
return $key;
}
// no level reached yet
return 0;
}
I have an associative array, ie
$primes = array(
2=>2,
3=>3,
5=>5,
7=>7,
11=>11,
13=>13,
17=>17,
// ...etc
);
then I do
// seek to first prime greater than 10000
reset($primes);
while(next($primes) < 10000) {}
prev($primes);
// iterate until target found
while($p = next($primes)) {
$res = doSomeCalculationsOn($p);
if( IsPrime($res) )
return $p;
}
The problem is that IsPrime also loops through the $primes array,
function IsPrime($num) {
global $primesto, $primes, $lastprime;
if ($primesto >= $num)
// using the assoc array lets me do this as a lookup
return isset($primes[$num]);
$root = (int) sqrt($num);
if ($primesto < $root)
CalcPrimesTo($root);
foreach($primes as $p) { // <- Danger, Will Robinson!
if( $num % $p == 0 )
return false;
if ($p >= $root)
break;
}
return true;
}
which trashes the array pointer I am iterating on.
I would like to be able to save and restore the array's internal pointer in the IsPrime() function so it doesn't have this side effect. Is there any way to do this?
You can "save" the state of the array:
$state = key($array);
And "restore" (not sure if there's a better method):
reset($array);
while(key($array) != $state)
next($array);
Don't rely on array pointers. Use iterators instead.
You can replace your outer code with:
foreach ($primes as $p) {
if ($p > 10000 && IsPrime(doSomeCalculationsOn($p))) {
return $p;
}
}
If speed is not an issue and you aren't pushing php memory limits the quickest solution is just to duplicate your primes array and iterate 2 different ones.
$awesomePrimes=$primes;
Then change globals and foreach in your function to $awesomePrimes
How about doing one more array of int -> int, where the the index is a running number from 0 to n and the value is the index of the associative array? So, you would have:
$pointer = array(
0 => 2,
1 => 3,
2 => 5,
// ...
);
and instead of referring directly to $prime you would use $prime[$pointer[$i]], or something similar?
use a "for" loop for one of your iterations. for example use this loop in your IsPrime method:
$primesLength = count($primes); // this is to avoid calling of count() so many times.
for ($counter=0 ; $counter < $primesLength ; $counter++) {
$p = $primesLength[$counter];
if( $num % $p == 0 )
return false;
if ($p >= $root)
break;
}
this way the internal array pointer will not be used in the method.