How to subtract number from array values using php? - php

I have this array.
$val = array(1, 0, 2, 1, 1);
I want to subtract 3.5 from $val array in the way that from every element that element not become negative like this:
$val = array(0, 0, 0, 0.5, 1)

Iterate array items and in loop check if target value is great that loop item, subtract item value from target value. If loop item is great than target value, subtract target value from loop item.
$val = array(1, 0, 2, 1, 1);
$subtract = 3.5;
foreach ($val as $key => $item){
if ($subtract >= $item){
$subtract -= $item;
$val[$key] = 0;
} else {
$val[$key] -= $subtract;
$subtract = 0;
}
}
See result in demo

One other possible approach: reduce the subtract value by the value of the current iteration, then set the current value to either zero or -$subtract. Break when $subtract drops below zero.
foreach ($val as &$number) {
$subtract -= $number;
$number = ($subtract > 0) ? 0 : -$subtract;
if ($subtract <= 0) break;
}

Related

Remove duplicate values just once and then sum array

I want to leave duplicates inside my array and only delete one occurrence when a value is found more than once.
Given an array of:
$array = ['+5', '+5', '+3', '+3', '+3', '+3', '+5', '+5'];
+5 and +3 each occur four times. I want to remove just one of the +5 values and just one of the +3 values, then find the sum of the remaining values.
$duplicate = ['+5', '+3'];
$array = ['+5', '+5', '+3', '+3', '+3', '+3', '+5', '+5'];
$i = 0;
foreach ($duplicate as $dup) {
if (strpos($duplicate[$i], '+') !== false) {
$duplicate[$i] = preg_replace('/[^0-9,:]/', '', $duplicate[$i]);
$duplicate[$i] = "-$duplicate[$i]";
}
$i++;
}
$sum = array_merge($duplicate, $array);
$end_value = array_sum(array_values($sum));
var_export($end_value);
For my input, the final sum should be 24 (15 + 9).
You need to remember what value you deleted from the array.
In below script you save te value if it apperas at the first time, in $existsValues array. If you find the same value again you delete it (and save information that you did it in $deletedValues array). If value exists in both arrays then you just do nothing with it. In this way you delete always the second occurence of the value and nothing more.
$existsValues = [];
$deletedValues = [];
foreach ($add_array as $key => $value) {
if (!in_array($value, $existsValues)) {
$existsValues[] = $value;
} else {
if (!in_array($value, $deletedValues)) {
$deletedValues[] = $value;
unset($add_array[$key]);
}
}
}
The task of summing all values and omitting just one occurrence of all repeated values can be done without conditionally maintaining a duplicate array and definitely doesn't require regex.
Code: (Demo)
$array = ['+5', '+5', '+3', '+3', '+3', '+3', '+5', '+5'];
foreach (array_count_values($array) as $value => $count) {
if ($count > 1) {
unset($array[array_search($value, $array)]);
}
}
var_export(array_sum($array)); // 24
The above groups values and counts their occurrences. Then it removes just one of the values when the values occurs more than once. Then sum the altered array.
Or you can boil it down to a mathematical process: (Demo)
$total = 0;
foreach (array_count_values($array) as $value => $count) {
$total += $value * ($count - ($count > 1));
}
echo $total;
// 24
^ if the count is greater than 1, subtract 1 from the multiplier. (if $count > 1, then true is treated as 1; otherwise 0).

How to subtract value of array but still in position

How can I subtract the value of an array if that value is greater than specific data?
For example:
I have an array of menu's position [0,3,10,5,6,9,2,7,1,4,8,11] I want to subtract all values that are greater than selectedDeletedNumbers and stay in their position.
Example: If I delete 3 and 5. I am using checkbox to delete
And the output should be like this.
[0,8,4,7,2,5,1,3,6,9]
Greater than 3 is subtract by 1, and greater than 5 is subtract by 2 since I selected 2 numbers.
HTML
#foreach ($columns as $key => $column)
<div class="checkbox checkbox-danger col-xs-6 el{{ $column->Field }}">
<input type="checkbox" name="CheckboxDelete[]" id="check{{ $column->Field }}" data-column="{{ $key }}" value="{{ $column->Field }}">
<label for="check{{ $column->Field }}"><b style="color: grey">{{ strtoupper($column->Field) }}</b></label>
</div>
#endforeach
Here is my code, but it's only when deleted 1, and not in a position as well.
$searchArr = array_search( $selectedDeletedNumbers, $items);
unset($items[$searchArr]);
$numbers = [];
foreach ($items as $item) {
if ($item > $col[1])
$numbers[] = $item - 1;
}
Thanks in advance.
For clarity and brevity, I'll refer to your input data as:
$array = [0, 3, 10, 5, 6, 9, 2, 7, 1, 4, 8, 11];
$deletes = [3, 5];
There is no sorting or preparation required for either of my snippets.
Output for both of the following are:
array (
0 => 0,
1 => 8,
2 => 4,
3 => 7,
4 => 2,
5 => 5,
6 => 1,
7 => 3,
8 => 6,
9 => 9,
)
Mostly function based approach: (Demo)
foreach (array_diff($array, $deletes) as $value) {
$result[] = array_reduce(
$deletes,
function ($carry, $item) use ($value) {
return $carry - ($value > $item);
},
$value
);
}
var_export($result);
*note: ($value > $item) will evaluated as true or false, when used in arithmetic, true = 1 and false = 0.
Language construct based approach: (Demo)
foreach ($array as $value) {
foreach ($deletes as $delete) {
if ($value == $delete) {
continue 2; // goto next value in outer loop without pushing
}
if ($value > $delete) {
--$value; // decrement value
}
}
$result[] = $value; // push adjusted value into result array
}
var_export($result);
The advantage in this snippet is that there are zero function calls and minimal variables declared.
To delete numbers from array and to subtract a count from the current number in the original array with the number of numbers smaller than it in selectedDeletedNumbers, you can:
Sort the selectedDeletedNumbers array.
Iterate over the original array and use binary search to get the count of numbers smaller than the current number in the original array and subtract it later.
If current number is present in selectedDeletedNumbers, then unset them.
Snippet:
<?php
$arr = [0,3,10,5,6,9,2,7,1,4,8,11];
$selectedDeletedNumbers = [3,5];
sort($selectedDeletedNumbers);
foreach($arr as $index => $val){
$low = 0;$high = count($selectedDeletedNumbers) - 1;
$equal_found = false;
while($low <= $high){
$mid = intval(($low + $high) / 2);
if($selectedDeletedNumbers[$mid] > $val){
$high = $mid - 1;
}else if($selectedDeletedNumbers[$mid] < $val){
$low = $mid + 1;
}else{
$equal_found = true;
unset($arr[$index]);
break;
}
}
if(!$equal_found){
$arr[$index] -= $low;
}
}
print_r($arr);
Demo: http://sandbox.onlinephpfunctions.com/code/ca881fce6a27c216f24a1701190940a87132ab1c
This way, the time complexity will be max(O(m log(m)),O(n log(m))) where n is size of original array and m is the size of selectedDeletedNumbers and space complexity is O(1).
If you want to re-index the numbers sequentially, do an $arr = array_values($arr) in the end.
One more solution is to optimize the checking of what to delete. This code makes sure the delete list is a 0 based array, so in the example it ensures it's
[ 0 => 3, 1 => 5 ]
Then it sorts it descending, but keeps the key, so
[ 1 => 5, 0 => 3 ]
Then when checking the numbers, if the number is greater than 5, it subtracts the key+1, so 1+1 = 2 (sorry for the basic maths). It can then stop and doesn't have to check any more.
$menu = [0,3,10,5,6,9,2,7,1,4,8,11];
$selectedDeletedNumbers = [3,5];
// In case array is not just 0 based index, can remove if it will be
$selectedDeletedNumbers = array_values($selectedDeletedNumbers);
// Sort so highest first, keep key (this is the offset)
arsort($selectedDeletedNumbers);
foreach ( $menu as $key => $menuItem ) {
foreach ( $selectedDeletedNumbers as $deleteIndex => $delete ) {
// Matching, just remove and carry on
if ( $menuItem == $delete ) {
unset($menu[$key]);
break;
}
// Greater than item, so deduct index and move on
if ( $menuItem > $delete ) {
$menu[$key] -= ($deleteIndex+1);
break;
}
}
}
print_r($menu);
If your selected array might come in any order, you can replace the
$selectedDeletedNumbers = array_values($selectedDeletedNumbers);
line with...
sort($selectedDeletedNumbers);
for ($i = 0; $i < count($menuPositions); $i++) {
for ($j = 0; $j < count($selectedDeletedNumbers); $j++) {
if ($menuPositions[$i] == $selectedDeletedNumbers[$j]) {
unset($menuPositions[$i]); // remove the match value from array
$menuPositions = array_values($menuPositions); // reorganize array keeping only the values are not null
$i--; //include the new value of $menuPositions[$i] on interation
break;
}
if ($menuPositions[$i] > $selectedDeletedNumbers[$j] ) {
$menuPositions[$i]--;
}
}
}

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,
)

Calculating the average increase from array values in PHP

I need to calculate the average increase of array values, and I've made a little script which works, but I'd like to know if:
There is a better, more efficient way of doing this
My logic is correct here in calculating the average increase
Lets say I have an array like so:
$array = array(5,10,15,10,0,15);
Lets also imagine that each array item is 1 day, and the value is some counter for that day. I would like to calculate the average increase/decrease in the counter.
What I've done, is looped through the array, and altered the values so that the current item = current item - previous item, what way I'm left with an array which would look like so:
$array = array(5,5,-5,-10,15);
Then I calculate the average as per normal, which in this example would give me a 2 average increase on a daily basis.
Code here:
$array = array(5,10,15,10,0,15);
$count = count($array);
for($i=0;$i<$count;$i++) {
if($i==0) {
$value = $array[$i];
unset($array[$i]);
}
else {
$tmp = $array[$i];
$array[$i] -= $value;
$value = $tmp;
}
}
echo array_sum($array) / count($array);
Is the logic correct here, and is there a more efficient way of doing this, maybe without the loop?
Thanks in advance :)
EDIT: Updated code to account for excluding first value
How about this:
function moving_average($array) {
for ($i = 1; $i < sizeof($array); $i++) {
$result[] = $array[$i] - $array[$i-1];
}
return array_sum($result)/count($result);
}
Try this :
$array = array(5,10,15,10,0,15);
$array2 = $array;
array_pop($array2);
array_unshift($array2, $array[0]);
$subtracted = array_map(function ($x, $y) { return $y-$x; } , $array2, $array);
array_shift($subtracted); /// Comment this if you want six values with 0 as first value
echo array_sum($subtracted) / count($subtracted);
Here's a snazzy one-liner for you:
$days = array(5, 10, 15, 10, 0, 15);
$deltas = array_slice(array_map(function($day1, $day2) {
return $day2 - $day1;
}, $days, array_slice($days, 1)), 0, -1);
var_dump(array_sum($deltas) / count($deltas));
$array = array(5,10,15,10,0,15);
list($prevVal) = array_slice($array, 1);
array_walk($array, function($value, $key, &$prevVal) use(&$array){
if ($key==0) { return; }
$array[$key] = ($value - $prevVal);
$prevVal = $value;
}, $prevVal);
echo array_sum($array) / count($array);
Outputs 1.6666666666667 in float(3.0994415283203E-5)

Categories