Remove array elements that are less than X - php

I have arrays:
$arr1 = array(5, 3, 9, 11, 6, 15);
$arr2 = array(11, 20, 1, 3, 8);
Now I need to loop through $arr1 and find the largest number that is less than X:
foreach($arr1 as $x) {
//need element that is MAX in $arr2 but is less than $x
}
so for example for the first run when $x = 5, maximum in $arr2 is 3 that is less than 5.
Is it possible to do this without having nested loop? I do not want to loop through $arr2. I tried using array_filter but didnt really work. Maybe I used it wrong.
This is what I tried with array_filter:
$results = array();
foreach($arr1 as $x) {
$max = max(array_filter($arr2, function ($x) { return $x < $y; }));
$results[$x] = $max;
}
The result should be this:
5 => 3,
3 => 1,
9 => 8,
11 => 8,
6 => 3,
15 => 11

The problem is with the use of the lambda - PHP does not automatically capture variables in the enclosing scope. (The official Anonymous Functions is a bit sparse on the topic, so see In PHP 5.3.0, what is the function "use" identifier? as well.)
Compare the original, which returns an empty array as it is effectively $x < undefined
$arr2 = array(11, 20, 1, 3, 8);
$y = 5;
var_dump(array_filter($arr2, function ($x) { return $x < $y; }));
with the following that employs the use syntax to bind the variable in the function
$arr2 = array(11, 20, 1, 3, 8);
$y = 5;
var_dump(array_filter($arr2, function ($x) use ($y) { return $x < $y; }));
(Also, in the original code there was no $y variable at all, whoops!)

Here's a method that uses array_map() to return the values that are lower than your maximum variable, then uses max() to return the highest (as in your example).
In this example I've used $var as a variable by reference (the &), so that it can be used by the get_highest() callback function which is accessing it as a global variable.
function get_highest($x) {
global $var;
return $x < $var ? $x : 0;
}
$results = array();
foreach($arr1 as &$var) {
$results[$var] = max(array_map('get_highest', $arr2));
}
Without using global variables
You can modify this if you don't want to use global variables by passing in an array of parameters to array_map(). It's a little strange the way it works, because the parameter array needs to be the same length as the original array, so I've used array_fill() to fill it up to the required length with the current value, so the callback function can use it to compare:
function get_highest($x, $y) {
return $x < $y ? $x : 0;
}
$results = array();
foreach($arr1 as $var) {
$max = array_fill(0, count($arr2), $var);
$results[$var] = max(array_map('get_highest', $arr2, $max));
}

Any solution that reuses/resets arr2 will be of quadratic complexity, meaning that if you have an arr1 of size N and an arr2 of size M, you'll be doing around N*M operations.
Plus function calls and parameter passing in case of lambda functions.
A better strategy if N and M are very large is to sort both arrays (complexity is N log N + M log M), then use the same strategy as merge sort to loop through the arrays, saving their state. This will execute in at most N+M operations instead of N*M, making the overall complexity linearithmic.
The lambda solution is easier to understand, more robust and simpler to maintain, so unless there's a pressing reason (N and M in the tens of thousands), it is to be preferred. You are trading speed for ease of maintenance; but it is normally a sweet deal.
First you sort both arrays with PHP's array sort functions, and get
$arr1 = array(3, 5, 6, 9, 11, 15);
$arr2 = array(1, 3, 8, 11, 20);
$map = array();
$n = count($arr1);
$m = count($arr2);
for ($i = 0, $j = 0, $y = false; $i < $n;) {
if ($j == $m) {
$map[$arr1[$i++]] = $y;
} else {
if ($arr1[$i] <= $arr2[$j]) {
$map[$arr1[$i++]] = $y;
} else {
$y = $arr2[$j++];
}
}
}

Related

How to get minimum positive value greater then 0 in php

I want to find minimum value from an array and subtract that value from all the elements until all the elements becomes 0. The problem is that if the array contains one of the element as 0 then it doesn't reflect anything. Thanks in advance.
<?php
$handle = fopen ("php://stdin","r");
fscanf($handle,"%d",$n);
$arr_temp = fgets($handle);
$arr = explode(" ",$arr_temp);
array_walk($arr,'intval');
for($j=0;$j<10;$j++)
{
$min = min($arr);
for($i=0;$i<count($arr);$i++)
{
if($arr[$i]>=$min)
$arr[$i]-=$min;
echo $arr[$i]." ";
}
echo "\n";
}
?>
Using array_filter(), plus a function you can get the minimum positive non-zero integer even if an array contains elements whose value is zero, as follows:
<?php
$arr = array(0, 1, 2, 3, 4, 0, 5, 6, 4, 3, 2, 1,0);
function getMin( $a ) {
$arr_filtered = array_values(array_filter( $a ));
return min($arr_filtered);
}
$min = getMin( $arr );
$count = count($arr);
while( count( array_unique($arr) ) > 1 ) {
for( $i = 0; $i < $count; $i++)
{
if( $arr[$i] >= $min ){
$arr[$i] -= $min;
echo $arr[$i]." ";
}
}
echo "\n";
}
See demo
Note, the code re-indexes the filtered array in getMin() by using array_values().
As long as the minimum value is one the above script works well. However, if the minimum value were another number, then the code needs to be adjusted. The following script handles a multi-dimensional array composed of four arrays, each with a different minimum value, including one with negative values:
<?php
error_reporting(E_ALL);
$a = [ [0,1, 2, 3, 4, 0, 5, 6, 4, 3, 2, 1,0],
[0, 2, 3, 4, 0, 5, 6, 4, 3, 2, 0],
[0, 3, 4, 0, 5, 6, 7,8,4, 3, 0],
[-1,0,1,2,3,4,2,1,0,-1]
];
function getMin( $a ) {
return min($a);
}
foreach ($a as $arr) {
while( ( $arr != null ) ) {
$arr = array_values(array_filter($arr));
if ($arr == null) {
break;
}
$min = getMin( $arr );
foreach ($arr as &$e) {
$e -= $min;
}
unset($e);
echo join(" ",$arr),"\n";
}
echo "\n";
}
see demo
Note, the second script explicitly filters out the zero values in each array whereas the first script effectively does so. The primary difference between the two scripts is that getMin() needs to be in the while loop so that the subtraction correctly occurs when the minimum number is greater than one or is a negative number.
The script also simplifies the code in several ways. Much more simple condition for the while loop -- works as long as array is not null. The code no longer needs getCount() since I changed the subtraction loop to use a foreach with a reference variable which is subsequently unset -- important to do to avoid problems. This foreach loop also does not require the if conditional of the first script.
It is because you taking each and every time $min = min($arr); value and it became 0 thats why your code is not working.
try below code
$min = min($arr);
for($j=0;$j<=100;$j++)
{
if (count(array_unique($arr)) === 1 && end($arr) === 0) {
break;
}
for($i=0;$i<count($arr);$i++)
{
if($arr[$i] >= $min){
$arr[$i]-=$min;
echo $arr[$i]." ";
}
}
echo "<br>";
}
I do not know much about php but in the first for loop you should check the elements of the array if they are zero or not using if statements e.g if(arr[j]!=0)
for($j=0;$j<10;$j++)
{
if($arr[$j]!=$0)
$min = min($arr);
for($i=0;$i<count($arr);$i++)
{
if($arr[$i]>=$min)
$arr[$i]-=$min;
echo $arr[$i]." ";
}
echo "\n";
}

Need Three Highest Values (or more, if tied) of PHP Array and Their Location?

I have an array. I'd like to get the three highest values of the array, but also remember which part of the array it was in.
For example, if my array is [12,3,7,19,24], my result should be values 24,19,12, at locations 4, 0, 3.
How do I do that? The first part is easy. Getting the locations is difficult.
Secondly, I'd like to also use the top three OR top number after three, if some are tied. So, for example, if I have [18,18,17,17,4], I'd like to display 18, 18, 17, and 17, at location 0,1,2,3.
Does that make sense? Is there an easy way to do that?
Wouldn't you be there using asort()?
For example:
<?php
$list = [4,18,18,17,17];
// Sort maintaining indexes.
asort($list);
// Slice the first 3 elements from the array.
$top3 = array_slice($list, -3, null, true);
// Results in: [ 1 => 18, 2 => 18, 3 => 17 ]
Or you can use arsort
function getMyTop($list, $offset, $top) {
arsort($list);
return array_slice($list, $offset, $top, true);
}
$myTop = getMyTop($list, 0, 3);
$myNextTop = getMyTop($list, 3, 4);
This is what you need!
<?php
$array = array(12,3,7,19,24);
$array_processed = array();
$highest_index = 0;
while($highest_index < 3)
{
$max = max($array);
$index = array_search($max,$array);
$array_processed[$index] = $max;
unset($array[$index]);
$highest_index++;
}
print_r($array_processed);
?>
You will get Index as well as the value! You just have to define how many top values you want! Let me know if it's what you want!
function top_three_positions($array){
// Sort the array from max to min
arsort($array);
// Unset everything in sorted array after the first three elements
$count = 0;
foreach($array as $key => $ar){
if($count > 2){
unset($array[$key]);
}
$count++;
}
// Return array with top 3 values with their indexes preserved.
return $array;
}
You can use a loop to determine how many elements your top-three-with-ties will have, after applying arsort:
function getTop($arr, $num = 3) {
arsort($arr);
foreach(array_values($arr) as $i => $v) {
if ($i >= $num && $v !== $prev) return array_slice($arr, 0, $i, true);
$prev = $v;
}
return $arr;
}
// Sample input
$arr = [4,18,17,6,17,18,9];
$top = getTop($arr, 3);
print_r($top); // [5 => 18, 1 => 18, 4 => 17, 2 => 17]
try this:
public function getTopSortedThree(array $data, $n = 3, $asc = true)
{
if ($asc) {
uasort($data, function ($a, $b) { return $a>$b;});
} else {
uasort($data, function ($a, $b) { return $a<$b;});
}
$count = 0;
$result = [];
foreach ($data as $key => $value) {
$result[] = $data[$key];
$count++;
if ($count >= $n){
break;
}
}
return $result;
}
Send false for desc order and nothing for asc order
Send $n with number of top values you want.
This functionality doesn't losing keys.
This task merely calls for a descending sort, retention of the top three values, and in the case of values after the third-positioned value being equal to the third value, retain these as well.
After calling rsort(), call a for() loop starting from the fourth element ([3]). If the current value is not equal to the value in the third position, stop iterating, and isolate the elements from the front of the array to the previous iteration's index. Done.
p.s. If the input array has 3 or fewer elements, the for() loop is never entered and the whole (short) array avoids truncation after being sorted.
Code: (Demo)
$array = [18, 17, 4, 18, 17, 16, 17];
rsort($array);
for ($i = 3, $count = count($array); $i < $count; ++$i) {
if ($array[2] != $array[$i]) {
$array = array_slice($array, 0, $i);
break;
}
}
var_export($array);
Because the loop purely finds the appropriate finishing point of the array ($i), this could also be compacted to: (Demo)
rsort($array);
for ($i = 3, $count = count($array); $i < $count && $array[2] === $array[$i]; ++$i);
var_export(array_slice($array, 0, $i));
Or slightly reduced further to: (Demo)
rsort($array);
for ($i = 3; isset($array[2], $array[$i]) && $array[2] === $array[$i]; ++$i);
var_export(array_slice($array, 0, $i));
Output:
array (
0 => 18,
1 => 18,
2 => 17,
3 => 17,
4 => 17,
)

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]);

Merge two flat indexed arrays of equal size so that values are pushed into the result in an alternating fashion

Suppose I have two arrays:
$a1 = array(0, 1, 2);
$a2 = array(3, 4, 5);
I want to be able to do a merge technique that alternates the array values and not just concatenate them. I want this result:
array(0, 3, 1, 4, 2, 5);
Is there a native way to do this as performance is an issue here since I need to do this thousands of times
Please note, I know I can do it like this:
for (var $i = 0; $i < count($a1); $i++) {
newArray[] = $a1[$i];
newArray[] = $b1[$i];
}
I'm looking for a built in way if there is a faster one.
$count = count($a1);
for ($i = 0; $i < $count; $i++) {
$newArray[] = $a1[$i];
$newArray[] = $b1[$i];
}
My work here is done.
$a1 = array(0,1,2);
$a2 = array(3,4,5);
$start = microtime(TRUE);
for($t = 0; $t < 100000; $t++)
{
$newArray = array();
$count = count($a1);
for ($i = 0; $i < $count; $i++)
{
$newArray[] = $a1[$i];
$newArray[] = $a2[$i];
}
}
echo round(microtime(TRUE) - $start, 2); # 0.6
$a1 = array(0,1,2);
$a2 = array(3,4,5);
$start = microtime(TRUE);
for($t = 0; $t < 100000; $t++)
{
$newArray = array();
for ($i = 0; $i < count($a1); $i++)
{
$newArray[] = $a1[$i];
$newArray[] = $a2[$i];
}
}
echo round(microtime(TRUE) - $start, 2); # 0.85
So pre-counting array size will be ~1/4 [citation needed] (on freakin' 100.000 iterations you will gain 0.2 in total) faster. If you put count() inside loop, it will recount on every iteration. 1/4 seems to me a reasonably faster. If you are looking for compiled function, you can stop.
P.S. Benchmark is like bikini, it shows you everything, and nothing.
Since you are "zippering" two indexed arrays, you can "transpose and flatten". I expect that this will not be quite as fast as using a for() or foreach(), but on small input arrays, there will be no noticeable drag on performance. In other words, when coding style or minimal declared variables is sought, then the following technique should be considered.
Code: (Demo)
$a1 = [0, 1, 2];
$a2 = [3, 4, 5];
var_export(
array_merge(...array_map(null, $a1, $a2))
);
Output:
array (
0 => 0,
1 => 3,
2 => 1,
3 => 4,
4 => 2,
5 => 5,
)
As a funky little function-less approach, you can push the $a1 value from the foreach() value declaration and inside the loop's body, you can push the $a2 value. Feast your eyes... (Demo)
$result = [];
foreach ($a1 as $index => $result[]) {
$result[] = $a2[$index];
}
var_export($result);
// same output as earlier snippet
For anyone that is hunting for an associative-safe technique, you could make looped slice or splice calls. Be aware that splice() will mutate/consume the input arrays in the process. (Slice Demo) (Splice Demo)
$result = [];
for ($i = 0, $count = count($a1); $i < $count; ++$i) {
$result += array_slice($a1, $i, 1)
+ array_slice($a2, $i, 1);
}
or
$result = [];
while($a1 && $a2) {
$result += array_splice($a1, 0, 1)
+ array_splice($a2, 0, 1);
}
p.s. I even build this utility snippet to offer more dynamic tooling of how many elements should be inserted and how frequently from each array while merging. See Insert elements from one array (one-at-a-time) after every second element of another array (un-even zippering).

Counting Sort in PHP

In a PHP project I have some data I want to sort using a linear time, simple counting sort:
$ar = array(7, 2, 0, 3, 8, 0, 12, 7, 6, 7);
$count = array();
foreach ($ar as $v)
$count[$v]++;
$sorted = array();
foreach ($count as $v => $c)
for ($i = 0; $i < $c; $i++)
$sorted[] = $v;
The problem is that the above obviously doesn't work. The php array works more like a hashmap than an array. The code can be made to work by inserting ksort($count) before the final loop, but ksort runs in O(nlogn) which destroys the entire point.
Is there any way to do a linear time sort in php? Perhaps using some paramter to array(), or some entirely different structure?
You didn't follow the algorithm correctly. This is O(n).
$ar = array(7, 2, 0, 3, 8, 0, 12, 7, 6, 7);
$count = array();
foreach ($ar as $v) {
$count[$v] = isset($count[$v]) ? $count[$v] + 1 : 1;
}
$sorted = array();
$min = min($ar);
$max = max($ar);
for ($i=$min; $i<=$max; $i++) {
if (isset($count[$i])) {
for ($j=0; $j<$count[$i]; $j++) {
$sorted[] = $i;
}
}
}
also, see array_count_values(), or alternatively compute the min and max inside the counting loop.
Approved answer is wrong. Correct:
$ar = array(7, 2, 0, 3, 8, 0, 12, 7, 6, 7);
$count = array();
foreach ($ar as $v) {
$count[$v] = isset($count[$v]) ? $count[$v] + 1 : 1;
}
$sorted = array();
$min = min($ar);
$max = max($ar);
for ($i=$min; $i <= $max; $i++) {
if (isset($count[$i])) {
for ($j=0; $j<$count[$i]; $j++) {
$sorted[] = $i;
}
}
}
If i understand your question AND comment correctly, just using sort($count) would work no?
$ar = array(7, 2, 0, 3, 8, 0, 12, 7, 6, 7);
$sorted = $ar;
sort($sorted);
var_dump($ar);
var_dump($sorted);
Result:
array(7,2,0,3,8,0,12,7,6,7);
array(0,0,2,3,6,7,7,7,8,12);
But i'm wondering what the foreach($ar as $v)$count[$v]++; does... doesn't really make sense...
Adding some comments to the code to show you why this doesn't do what you think it should do.
$ar = array(7, 2, 0, 3, 8, 0, 12, 7, 6, 7);
$count = array();
foreach ($ar as $v) {
// add each number in $ar to $count.
// the first number in $ar is 7, so 7 will be the first number in $count.
// because 7 is in $ar 3 times, $count[7] == 3.
$count[$v]++;
}
// the output of print_r will be very revealing:
print_r($count);
/*Array
(
[7] => 3
[2] => 1
[0] => 2
[3] => 1
[8] => 1
[12] => 1
[6] => 1
)*/
$sorted = array();
foreach ($count as $v => $c) {
// the first entry: $count[7] == 3
// so add 7 to $sorted 3 times.
// the second entry: $count[2] == 1
// so add 2 to $sorted 1 time.
// etc.
for ($i = 0; $i < $c; $i++) {
$sorted[] = $v;
}
}
This simply groups numbers together based on their location in the first array.
To get the sorted $ar in a variable of it's own ($sorted), it's pretty trivial:
$sorted = $ar;
sort($sorted);
Which makes me think that your question and comment is not giving the whole picture.
Edit: Now after you have clarified that you wanted to implement a specific algorithm (and you actually got an answer already that shows some points that were wrong implementing it first), I think it's worth to focus on another aspect of your question:
You're comparing the complexity of two (theoretical) algorithms, but you're leaving aside how the algorithms are implemented.
PHP's sort() - even based on "bad" quicksort, will outrun your own PHP usercode implementation of some other algorithm by numbers.
You just have compared the wrong parameters here. The complexity of a function does not says much when you compare a build-in PHP function with some function in your user-code.
$A = [1, 2, 2, 2, 1, 3, 3, 1, 2, 4, 5, 0, 0]; // example array
$m = max($A);
$count = array_fill(0, $m + 1, '0');
foreach ($A as $value) $count[$value] += 1;
// next step is print the numbers
$a = [];
foreach ($count as $key => $value) {
for ($i = 0; $i < $value;) {
array_push($a, $key);;
$i++;
}
}
var_dump($count); // print the sorted array
var_dump($a); // print the numbers (low to high)

Categories