Get no. of array elements between certain values in PHP? - php

How to the find the number of array elements present in the array between 2 values in PHP .
lets say this is my array =>
$a = array(1,2,3,5,10);
I want to find the length of array between 2 values i.e. 2 and 10. So the answer will be 3 in the case. If the highest value to be searched is present in the array it should be added in count.
also length of array between 2 and 9 is 2.
Hope I am clear with my question. Any help would be appreciated.

Use array_filter() to filter the array down to the matching elements, then use count() on the resulting filtered array.
$a = array(1,2,3,5,10);
print count(array_filter($a, function($e) {return ($e>=2 && $e<=10);}));
Hope that helps.
Note: The syntax I've used here, with the embedded function, requires PHP v5.3 or higher.
[EDIT]
Turn it into a simple callable function:
$a = array(1,2,3,5,10);
print countInRange(2,10);
function countInRange($min,$max) {
return count(array_filter($a, function($e) use($min,$max) {return ($e>=$min && $e<=$max);}));
}
See PHP manual for more info on array_filter() function.

$count = 0;
for( $i = 0; $i < count($a); $i++ ) {
if( $a[$i] <= $max && $a[$i] >= $min )
$count++;
}
echo $count;

$count = 0;
$min = 2;
$max = 10;
$a = array(1,2,3,5,10);
foreach($a as $val) {
if($val > $min && $val <= $max) ++$count;
}
This should work, right? At least it will find all numbers, which are higher than your minimum but smaller/equal to your maximum.
Of course this also means, that an array of 1, 2, 9, 5 would return 2, since both 9 and 5 are greater than 2 and smaller than 10.

Related

How can I compare a loop value to an array value that is not sequentially the same?

I have a loop from 1 to 4, and I have an array which contain 1,2, and 4, but misses the number 3. How do I put or replace the missing 3 into 0 as my only thought to solve my problem, or get all the same values and then compare both sides? Because I need to compare both sides if it's same or not.
With this code, I'm getting the error: "undefined offset 3".
$a = array(1,2,4);
for ($i=1; $i <= 4; $i++) {
if ($i==$a[$i]) {
echo 'true';
} else {
false;
}
}
Is there other way to compare all those that have same value like
1 & 1 = true
2 & 2 = true
3 & = false
4 & 4 = true
And display something like
1. true
2. true
3. false
4. true
Probably the most straightforward way is to use in_array().
$a = [1, 2, 4];
for ($i = 1; $i <= 4; $i++) {
echo $i.'. '.(in_array($i, $a) ? 'true' : 'false').PHP_EOL;
}
Here is a working example.
Something like that :
$a = array(1,2,4);
$flipped = array_flip($a);
for($i = 1; $i <= 4; $i++){
$res[$i]="false";
if(array_key_exists($i, $flipped)){
$res[$i] = "true";
}
}
foreach($res as $key=>$value){
echo $key.". ".$value."<br/>";
}
The values in your array: $a = array(1, 2, 4) will not correspond to the $i in your for loop when you use $a[$i], because the value inside the square brackets refers to the key of the array, not to the value.
An array in PHP is an ordered map, with keys corresponding to values. When you define an array of values without assigning keys, like $a = array(1, 2, 4), PHP automatically creates integer keys for each of the values, beginning with zero, so in effect you are creating this array:
[0 => 1, 1 => 2, 2 => 4]
With the code you showed, you should be getting an undefined offset notice for 4 as well as 3.
If your array is sorted, one way to check for missing elements in your sequence is to check the current value of the array for each increment of your for loop, and move to the next element when a value matches.
$a = array(1,2,4);
for ($i=1; $i <= 4; $i++) {
if (current($a) == $i){
echo "$i: true".PHP_EOL;
next($a);
} else {
echo "$i: false".PHP_EOL;
}
}
The array must be sorted for this to work. If it is not in order as it is in your example, you can sort it with sort($a);.

Check if the array can be sorted with a single swap of 2 elements

I am trying to write a function that will check if the array can be sorted with a single swap of the values in the array.
For example: array(1,3,5,3,7) must return true, but array(1,3,5,3,4) must return false.
I tried the following code below, but I'm stuck with it:
$noOfIterations = 0;
for($x = 0; $x < count($a)-2; $x++) {
if($a[$x] > $a[$x+1]) {
$noOfIterations ++;
}
}
return $noOfIterations >1;
// The below solution helped as well.
//$arr = [1, 3, 5, 3, 7]; //[1, 3, 5, 3, 4]
$arr = [1, 3, 5, 3, 4];
$sortedArr = $arr;
sort($sortedArr);
print_r(array_intersect_assoc($arr,$sortedArr));
This should work for you:
(Here I first make a copy of the original array to then sort() it. After this I loop through both arrays with array_map() and look how many position has changed. With array_filter() I sort the elements out where no position has changed. Then you can simply check if 2 or more position has changed and print either FALSE or TRUE)
<?php
$arr = [1, 3, 5, 3, 7]; //[1, 3, 5, 3, 4]
$sortedArr = $arr;
sort($sortedArr);
$filtered = array_filter(
array_map(function($v1, $v2){
return ($v1 == $v2 ?FALSE:TRUE);
}, $arr, $sortedArr)
);
var_dump(count($filtered) > 2 ? FALSE : TRUE);
?>
output:
TRUE //FALSE
Execute the sort, then compare the original array with the sorted array using array_intersect_assoc().... if the difference is more than two elements, then the answer is 'no'
If you really wanna do it with a loop, you can do it with a double loop comparing each value to one another. You need to get a little creative with the comparing. From what I see, your code succeeds on the first array but fails on the second. It fails on the second one because you are only checking 2 adjacent entries and 3 is always less than 4. Or you can keep track of the largest number and count how many numbers are less than that value past it. Also make sure to add to the count if you encounter another bigger value. Hope all this makes sense.
What if array is sorted and no swap is needed?
It might help if I knew Why.
Try this, it works for your two example arrays.
function swap($array){
$prev = 0;
$count = 0;
foreach($array as $val){
if($val < $prev){
$count++;
}
else{
$prev = $val;
}
}
if($count < 2){return(true);}else{return(false);}
}
My answer in php.
function oneSwap($A){
$count=count($A);
$swaps=0;
$curr_max = 0;
$res = false;
for($i = 0; $i <= $count; $i++) {
if(isset($A[$i+1])){
if(($A[$i] >= $A[$i + 1]) && $curr_max >= $A[$i+1]){
$swaps++;
}
if($A[$i] >= $A[$i +1]){
$curr_max = $A[$i];
}
}
}
if(($swaps == 1) || ($swaps == 0 && $curr_max ==0)){
$res = true;
echo $res;
}
}
oneSwap([3,1,2,8]);
oneSwap([1,2,3]);
oneSwap([1,5,3,3,7]);
oneSwap([3,2,1,8]);
oneSwap([2,1,1,2]);

PHP Count 1 too many on Array as it's 0 based?

I've had this problem a few times now when for looping over an array item.
In this instance I'm generating all 2 letter combinations of the alphabet.
The code works (and I know there's a much easier way of doing it with 2 for loops, but I'm trying something different).
However I have to do count -1 as count() returns the number 26 for the array length, however the 26th item obviously doesn't exist as it's 0 based?
Is there not a version of count() that works on a zero-based basis?
<?php
$alphas = range('a', 'z');
$alphacount = count($alphas);
// Why do I have to do this bit here?
$alphaminus = $alphacount -1;
$a = 0;
for ($i=0;$i<$alphacount;$i++) {
$first = $alphas[$a];
$second = $alphas[$i];
if ($i === $alphaminus && $a < $alphaminus ) {
$i = 0;
$a ++;
}
echo "$first$second<br>";
}
?>
Without $alphaminus = $alphacount -1; I get undefined offset 26?
How about:
<?php
$alphas = range('a', 'z');
$alphacount = count($alphas);
$a = 0;
for ($i=0;$i<$alphacount;$i++) {
$first = $alphas[$a];
$second = $alphas[$i];
if ($i >= $alphacount && $a < $alphaminus ) {
$i = 0;
$a ++;
}
echo "$first$second<br>";
}
So you don't have to to -1 since you don't like it! :)
And how about:
$alphas = range('a', 'z');
for ($i = 0; $i < count($alphas); $i++) {
for ($a = 0; $a < count($alphas); $a++) {
echo "{$alphas[$i]}{$alphas[$a]}\n";
}
}
Or forget about arrays! This is more fun :)
array_walk($alphas, function ($a) use ($alphas) {
array_walk($alphas, function ($b) use ($a) {
print "$a$b\n";
});
});
The problem is that you reset $i to 0 in the loop; then on encountering the end of the loop $i is incremented, so the next run in the loop will be with $i = 1 instead of $i = 0.
That is, the next subrange of letters starts with (letter)b instead of (letter)a. (See your output: the next line after az is bb rather than ba.)
Solution: reset $i to -1 in the loop, then at the end it will run with the value 0 again.
You have 26 characters, but arrays in PHP are indexed from 0. So, indexes are 0, 1, ... 25.
count is 1-based and arrays created by range() are 0-based.
It means that:
$alphas[0] == a
$alphas[25] == z
$count($alphas) = 26; // there are 26 elements. First element is $alphas[0]
Why does it have to be so complicated? You could simply do
foreach ($alphas as $alpha)
{
foreach($alphas as $alpha2)
{
echo $alpha.$alpha2."<br>";
}
}
Note: It is mostly not a good idea to manipulate the loop counter variable inside the body of that very loop. You set $i to 0 on a certain condition. That could give you unexpected results, hence the reason why you have to navigate around it.

Given an array of integers, what's the most efficient way to get the number of other integers in the array within n?

Given the following array:
$arr = array(0,0,1,2,2,5,6,7,7,9,10,10);
And assuming $n = 2, what is the most efficient way to get a count of each value in the array within $n of each value?
For example, 6 has 3 other values within $n: 5,7,7.
Ultimately I'd like a corresponding array with simply the counts within $n, like so:
// 0,0,1,2,2,5,6,7,7,9,10,10 // $arr, so you can see it lined up
$count_arr = array(4,4,4,4,4,3,3,4,4,4, 2, 2);
Is a simple foreach loop the way to go? CodePad Link
$arr = array(0,0,1,2,2,5,6,7,7,9,10,10);
$n = 2;
$count_arr = array();
foreach ($arr as $v) {
$range = range(($v-$n),($v+$n)); // simple range between lower and upper bound
$count = count(array_intersect($arr,$range)); // count intersect array
$count_arr[] = $count-1; // subtract 1 so you don't count itself
}
print_r($arr);
print_r($count_arr);
My last answer was written without fully groking the problem...
Try sorting the array, before processing it, and leverage that when you run through it. This has a better runtime complexity.
$arr = array(0,0,1,2,2,5,6,7,7,9,10,10);
asort($arr);
$n = 2;
$cnt = count($arr);
$counts = array_pad(array(), $cnt, 0);
for ($x=0; $x<$cnt; $x++) {
$low = $x - 1;
$lower_range_bound = $arr[$x]-$n;
while($low >= 0 && ($arr[$low] >= $lower_range_bound)) {
$counts[$x]++;
$low--;
}
$high = $x + 1;
$upper_range_bound = $arr[$x]+$n;
while($high < $cnt && $arr[$high] <= $upper_range_bound) {
$counts[$x]++;
$high++;
}
}
print_r($arr);
print_r($counts);
Play with it here: http://codepad.org/JXlZNCxW

Randomize a PHP array with a seed?

I'm looking for a function that I can pass an array and a seed to in PHP and get back a "randomized" array. If I passed the same array and same seed again, I would get the same output.
I've tried this code
//sample array
$test = array(1,2,3,4,5,6);
//show the array
print_r($test);
//seed the random number generator
mt_srand('123');
//generate a random number based on that
echo mt_rand();
echo "\n";
//shuffle the array
shuffle($test);
//show the results
print_r($test);
But it does not seem to work. Any thoughts on the best way to do this?
This question dances around the issue but it's old and nobody has provided an actual answer on how to do it: Can i randomize an array by providing a seed and get the same order? - "Yes" - but how?
Update
The answers so far work with PHP 5.1 and 5.3, but not 5.2. Just so happens the machine I want to run this on is using 5.2.
Can anyone give an example without using mt_rand? It is "broken" in php 5.2 because it will not give the same sequence of random numbers based off the same seed. See the php mt_rand page and the bug tracker to learn about this issue.
Sorry, but accordingly to the
documentation the
shuffle function is seeded automatically.
Normally, you shouldn't try to come up with your own algorithms to randomize things since they are very likely to be biased. The Fisher-Yates algorithm is known to be both efficient and unbiased though:
function fisherYatesShuffle(&$items, $seed)
{
#mt_srand($seed);
for ($i = count($items) - 1; $i > 0; $i--)
{
$j = #mt_rand(0, $i);
$tmp = $items[$i];
$items[$i] = $items[$j];
$items[$j] = $tmp;
}
}
Example (PHP 5.5.9):
php > $original = array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
php > $shuffled = (array)$original;
php > fisherYatesShuffle($shuffled, 0);
php > print_r($shuffled);
Array
(
[0] => 6
[1] => 0
[2] => 7
[3] => 2
[4] => 9
[5] => 3
[6] => 1
[7] => 8
[8] => 5
[9] => 4
)
php > $shuffled = (array)$original;
php > fisherYatesShuffle($shuffled, 0);
php > print_r($shuffled);
Array
(
[0] => 6
[1] => 0
[2] => 7
[3] => 2
[4] => 9
[5] => 3
[6] => 1
[7] => 8
[8] => 5
[9] => 4
)
You can use array_multisort to order the array values by a second array of mt_rand values:
$arr = array(1,2,3,4,5,6);
mt_srand('123');
$order = array_map(create_function('$val', 'return mt_rand();'), range(1, count($arr)));
array_multisort($order, $arr);
var_dump($arr);
Here $order is an array of mt_rand values of the same length as $arr. array_multisort sorts the values of $order and orders the elements of $arr according to the order of the values of $order.
The problem you have is that PHP comes with two random number generators built in.
The shuffle() command does not use the mt_rand() random number generator; it uses the older rand() random number generator.
Therefore, if you want shuffle() to use a seeded number sequence, you need to seed the older randomiser, using srand() rather than mt_srand().
In most other cases, you should use mt_rand() rather than rand(), since it is a better random number generator.
The main question involves two parts. One is about how to shuffle. The other is about how to add randomness to it.
A simple solution
This is probably the simplest answer to the main question. It is sufficient for most cases in PHP scripting. But not all (see below).
function /*array*/ seedShuffle(/*one dimentional array*/ $array, /*integer*/ $seed) {
$tmp = array();
for ($rest = $count = count($array);$count>0;$count--) {
$seed %= $count;
$t = array_splice($array,$seed,1);
$tmp[] = $t[0];
$seed = $seed*$seed + $rest;
}
return $tmp;
}
The above method will do, even though it doesn't produce true random shuffles for all possible seed-array combinations. However, if you really want it to be balanced and all, I guess PHP shuldn't be your choice.
A more useful solution for advanced programmers
As stated by André Laszlo, randomization is a tricky business. It is usually best to let a dedicated object handle it. My point is, that you shouldn't need to bother with the randomness when you write the shuffle function. Depending on what degree of ramdomness you would like in your shuffle, you may have a number of PseudoRandom objects to choose from. Thus the above could look like this:
abstract class PseudoRandom {
protected abstract function /*integer*/ nextInt();
public function /*integer*/ randInt(/*integer*/ $limit) {
return $this->nextInt()%$limit;
}
}
function /*array*/ seedShuffle($array, /*PseudoRandom Object*/ $rnd) {
$tmp = array();
$count = count($array);
while($count>0) {
$t = array_splice($array,$rnd->randInt($count--),1);
$tmp[] = $t[0];
}
return $tmp;
}
Now, this solution is the one I would vote for. It separates shuffle codes from randomization codes. Depending on what kind of random you need you can subclass PseudoRandom, add the needed methods and your preferred formulas.
And, as the same shuffle function may be used with many random algorithms, one random algorithm may be used in different places.
In recent PHP versions, seeding the PHP builtin rand() and mt_rand() functions will not give you the same results everytime. The reason for this is not clear to me (why would you want to seed the function anyway if the result is different every time.) Anyway, it seems like the only solution is to write your own random function
class Random {
// random seed
private static $RSeed = 0;
// set seed
public static function seed($s = 0) {
self::$RSeed = abs(intval($s)) % 9999999 + 1;
self::num();
}
// generate random number
public static function num($min = 0, $max = 9999999) {
if (self::$RSeed == 0) self::seed(mt_rand());
self::$RSeed = (self::$RSeed * 125) % 2796203;
return self::$RSeed % ($max - $min + 1) + $min;
}
}
Usage:
// set seed
Random::seed(42);
// echo 10 numbers between 1 and 100
for ($i = 0; $i < 10; $i++) {
echo Random::num(1, 100) . '<br />';
}
The code above will output the folowing sequence every time you run it:
76
86
14
79
73
2
87
43
62
7
Just change the seed to get a completely different "random" sequence
A variant that also works with PHP version 7.2, because the php function create_function is deprecated in the newest php version.
mt_srand($seed);
$getMTRand = function () {
return mt_rand();
};
$order = array_map($getMTRand, range(1, count($array)));
array_multisort($order, $array);
return $array;
I guess this will do the job :
function choose_X_random_items($original_array , $number_of_items_wanted = -1 , $seed = FALSE ){
//save the keys
foreach ($original_array as $key => $value) {
$original_array[$key]['key_memory'] = $key;
}
$original_array = array_values($original_array);
$results = array();
if($seed !== FALSE){srand($seed);}
$main_random = rand();
$random = substr($main_random,0,( $number_of_items_wanted == -1 ? count($original_array) : min($number_of_items_wanted,count($original_array)) ));
$random = str_split($random);
foreach ($random AS $id => $value){
$pick = ($value*$main_random) % count($original_array);
$smaller_array[] = $original_array[$pick];
unset($original_array[$pick]);
$original_array = array_values($original_array);
}
//retrieve the keys
foreach ($smaller_array as $key => $value) {
$smaller_array[$value['key_memory']] = $value;
unset($smaller_array[$value['key_memory']]['key_memory']);
unset($smaller_array[$key]);
}
return $smaller_array;
}
In order to not limit the resulting array, set $number_of_items_wanted to -1
In order to not use a seed, set $seed to FALSE
Seeded shuffle while maintaining the key index:
function seeded_shuffle(array &$items, $seed = false) {
mt_srand($seed ? $seed : time());
$keys = array_keys($items);
$items = array_values($items);
for ($i = count($items) - 1; $i > 0; $i--) {
$j = mt_rand(0, $i);
list($items[$i], $items[$j]) = array($items[$j], $items[$i]);
list($keys[$i], $keys[$j]) = array($keys[$j], $keys[$i]);
}
$items = array_combine($keys, $items);
}
A simple solution:
$pool = [1, 2, 3, 4, 5, 6];
$seed = 'foo';
$randomIndex = crc32($seed) % count($pool);
$randomElement = $pool[$randomIndex];
It might not be quite as random as the Fisher Yates shuffle, but I found it gave me more than enough entropy for what I needed it for.
Based on #Gumbo, #Spudley, #AndreyP answers, it works like that:
$arr = array(1,2,3,4,5,6);
srand(123); //srand(124);
$order = array_map(function($val) {return rand();}, range(1, count($arr)));
array_multisort($order, $arr);
var_dump($arr);
Home Made function, using crc32(note:also returns negative value(https://www.php.net/manual/en/function.crc32.php))
$sortArrFromSeed = function($arr, $seed){
$arrLen = count($arr);
$newArr = [];
$hash = crc32($seed); // returns hash (0-9 numbers)
$hash = strval($hash);
while(strlen($hash) < $arrLen){
$hash .= $hash;
}
for ($i=0; $i<$arrLen; $i++) {
$index = (int) $hash[$i] * (count($arr)/9); // because 0-9 range
$index = (int) $index; // remove decimal
if($index !== 0) $index--;
array_push($newArr, $arr[$index]);
unset($arr[$index]);
$arr = array_values($arr);
}
return $newArr;
};
// TESTING
$arr = ['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'];
$arr = $sortArrFromSeed($arr,'myseed123');
echo '<pre>'.print_r($arr,true).'</pre>';
This seems the easiest for me...
srand(123);
usort($array,function($a,$b){return rand(-1,1);});

Categories