Remove duplicate values just once and then sum array - php

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

Related

Consolidate array of numbers without exceeding a predefined maximum value per element

I'm trying to combine numbers in an array by adding them so that the max value can only by 30.
For example, this is my array:
array(10,30,10,10,15);
After combining the numbers in the array to items with a max value 30, the result should be:
array(30,30,15);
How to achieve this?
I'm trying to combine numbers in an array by adding them so that the
max value can only by 30
So, when you combine numbers, you can achieve the lowest possible set of values in your array and also make sure that max value remains 30 by:
First, sort them.
Second, keeping adding elements to sum till you are about to get a sum > 30.
Third, once an element can no longer be added to a sum, add the current sum in your array and make the current element as the new sum.
Code:
<?php
$arr = array(10,30,10,10,15);
sort($arr);
$res = [];
$curr_sum = 0;
foreach($arr as $each_value){
if($curr_sum + $each_value <= 30) $curr_sum += $each_value;
else{
$res[] = $curr_sum;
$curr_sum = $each_value;
}
}
$res[] = $curr_sum;
print_r($res);
Demo: https://3v4l.org/BYhuE
Update: If order of the numbers matters, seeing your current output, you could just use rsort() to show them in descending order.
rsort($res);
$total = array_sum(array(10,30,10,10,15)); //assign sum totals from orignal array
$maxValue = 30; //assign max value allowed in array
$numberOfWholeOccurancesOfMaxValue = floor($total/$maxValue);
$remainder = $total%$maxValue;
//build array
$i=0;
while ( $i < $numberOfWholeOccurancesOfMaxValue ){
$array[] = $maxValue;
$i++;
}
$array[] = $remainder;
print_r($array);
You can loop only once to get this,
$temp = array(10,30,10,10,15);
natsort($temp); // sorting to reduce hustle and complication
$result = [];
$i = 0;
$maxValue = 30;
foreach($temp as $v){
// checking sum is greater or value is greater or $v is greater than equal to
if(!empty($result[$i]) && (($result[$i]+$v) > $maxValue)){
$i++;
}
$result[$i] = (!empty($result[$i]) ? ($result[$i]+$v) : $v);
}
print_r($result);
Working demo.
I believe finding most space-optimized/compact result requires a nested loop. My advice resembles the firstFitDecreasing() function in this answer of mine except in this case the nested loops are accessing the same array. I've added a couple of simple conditions to prevent needless iterations.
rsort($array);
foreach ($array as $k1 => &$v1) {
if ($v1 >= $limit) {
continue;
}
foreach ($array as $k2 => $v2) {
if ($k1 !== $k2 && $v1 + $v2 <= $limit) {
$v1 += $v2;
unset($array[$k2]);
if ($v1 === $limit) {
continue 2;
}
}
}
}
rsort($array);
var_export($array);
By putting larger numbers before smaller numbers before processing AND by attempting to add multiple subsequent values to earlier values, having fewer total elements in the result is possible.
See my comparative demonstration.
I believe #Clint's answer is misinterpreting the task and is damaging the data by summing all values then distributing the max amounts in the result array.
With more challenging input data like $array = [10,30,5,10,5,13,14,15,10,5]; and $limit = 30;, my solution provides a more dense result versus #nice_dev's and #rahul's answers.

How to add up the total of 1 array + the total of another array

I am a rookie beginner with PHP, i was wondering how i could add up the total number from 1 array + the total number of another array together. I managed to make this code with help from stackoverflow answers on google. I don't know why but it's no where explained or i am looking over it. Been looking for almost an hour to make this work. Here is the code:
<?php
$array = array(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19);
$odds = array();
$even = array();
foreach($array as $val) {
if($val % 2 == 0) {
$even[] = $val;
} else {
$odds[] = $val;
}
}
$array = array();
foreach($even as $key => $val) {
$array[] = $val;
if(isset($odds[$key])) {
$array[] = $odds[$key];
}
}
echo '<b>Oneven</b> ';
print_r($odds);
echo '<br><br><br>';
echo "Bovenstaande <b>oneven</b> getallen bijelkaar opgeteld = " . array_sum($odds) . "\n";
echo '<br><br><br><hr style="margin-top:2%;margin-bottom:4%;">';
/* Array nummer 2 */
$array = array(20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40);
$odds = array();
$even = array();
foreach($array as $val) {
if($val % 2 == 0) {
$even[] = $val;
} else {
$odds[] = $val;
}
}
$array = array();
foreach($even as $key => $val) {
$array[] = $val;
if(isset($odds[$key])) {
$array[] = $odds[$key];
}
}
echo '<b>Even</b> ';
print_r($even);
echo '<br><br><br>';
echo "Bovenstaande <b>even</b> getallen bijelkaar opgeteld = " . array_sum($even) . "\n";
?>
So i don't know how to do it in another way but i have array 1 code at first and then code 2 begins with another array.
The thing is that i want to make a program that includes the odd numbers from 1 to 19 and the even numbers from 20 to 40 and then count the total of those 2 array's. Is there a way to do this in 1 code and count up the total of those 2 array's together. I already have that part of code that it counts the array, in code 1 that is 100 and in code 2 it is 330.
330+100=430 that's the output that i want. Why is that so hard? haha...
I appreciate the help and time effort.
First off, there's a lot of complexity involved in creating the initial array and then extracting only the odd numbers. This complexity can be eliminating by using the range and array_filter functions like so:
$odds = array_filter(range(1, 19), function($elem) {
return $elem & 1;
});
$even = array_filter(range(20, 40), function($elem) {
return $elem % 2 == 0;
});
to calculate sum of the sum of odds plus the sum of even, you can simply merge them together and use array_sum in the same you are doing for the individual arrays
$totalSum = array_sum(array_merge($odds, $even))
As #Darragh pointed out in the comments, you can simplify the array creation by specifying a step parameter for the range function.
$odds = range(1, 19, 2) // start at 1, go up to 19, by increments of 2

How to substract in one array

I am still beginner.
I want to subtract a value in an array, then I want to compare values. I have an array, the values are not known, depend on the result of a function.
Example :
$value = [5,8,13,15];
I want to subtract each value and save it in an array. Example :
8-5 = 3
13-8 = 5
15-13 = 2
then I want to compare each value (3, 5, 2), which one is bigger.
Please help me. Thank you before.
$value = [5,18,13,15];
sort($value); //to not get negative results
$loop = 0;
$results = array();
while ($loop < count($value))
{
if ($loop == 0)
{
$loop++;
}
else
{
$firstval = $value[$loop];
$secondval = $value[$loop-1];
$results[] = intval($firstval) - intval($secondval);
$loop++;
}
}
sort($results);
$thebiggestkey = $results[count($results)-1];
This should do it for you
A small example with Indexes and Array, if you here is wishes it you can use Foreach to subtract all that there is in the Array
( http://php.net/manual/en/control-structures.foreach.php )
$value = [5,8,3,13,15];
$rep = $value[0] - $value[1];
//5 - 8
echo $rep;
//return -3

Get x highest values from an array including identical values

I'm using the following code to retrieve the highest 3 numbers from an array.
$a = array(1,2,5,10,15,20,10,15);
arsort($a, SORT_NUMERIC);
$highest = array_slice($a, 0, 3);
This code correctly gives me the highest three numbers array(20,15,10); however, I'm interested in getting the highest 3 numbers including the ones that are identical. In this example, I'm expecting to get an array like array(10, 10, 15, 15, 20)
Might be simpler but my brain is tired. Use arsort() to get the highest first, count the values to get unique keys with their count and slice the first 3 (make sure to pass true to preserve keys):
arsort($a, SORT_NUMERIC);
$counts = array_slice(array_count_values($a), 0, 3, true);
Then loop those 3 and fill an array with the number value the number of times it was counted and merge with the previous result:
$highest = array();
foreach($counts as $value => $count) {
$highest = array_merge($highest, array_fill(0, $count, $value));
}
You can use a function like this:
$a = array(1,2,5,10,15,20,10,15); //-- Original Array
function get3highest($a){
$h = array(); //-- highest
if(count($a) >= 3){ //-- Checking length
$c = 0; //-- Counter
while ($c < 3 || in_array($a[count($a)-1],$h) ){ //-- 3 elements or repeated value
$max = array_pop($a);
if(!in_array($max,$h)){
++$c;
}
$h[] = $max;
}
sort($h); //-- sorting
}
return $h; //-- values
}
print_r(get3Highest($a));
Of course you can improve this function to accept a dinamic value of "highest" values.
The below function may be usefull
$a = array(1,2,5,10,15,20,10,15);
function getMaxValue($array,$n){
$max_array = array(); // array to store all the max values
for($i=0;$i<$n;$i++){ // loop to get number of highest values
$keys = array_keys($array,max($array)); // get keys
if(is_array($keys)){ // if keys is array
foreach($keys as $v){ // loop array
$max_array[]=$array[$v]; // set values to max_array
unset($array[$v]); // unset the keys to get next max value
}
}else{ // if not array
$max_array[]=$array[$keys]; // set values to max_array
unset($array[$keys]); // unset the keys to get next max value
}
}
return $max_array;
}
$g = getMaxValue($a,3);
Out Put:
Array
(
[0] => 20
[1] => 15
[2] => 15
[3] => 10
[4] => 10
)
You can modify it to add conditions.
I thought of a couple of other possibilities.
First one:
Find the lowest of the top three values
$min = array_slice(array_unique($a, SORT_NUMERIC), -3)[0];
Filter out any lower values
$top3 = array_filter($a, function($x) use ($min) { return $x >= $min; });
Sort the result
sort($top3);
Advantages: less code
Disadvantages: less inefficient (sorts, iterates the entire array, sorts the result)
Second one:
Sort the array in reverse order
rsort($a);
Iterate the array, appending items to your result array until you've appended three distinct items.
$n = 0;
$prev = null;
$top = [];
foreach ($a as $x) {
if ($x != $prev) $n++;
if ($n > 3) break;
$top[] = $x;
$prev = $x;
}
Advantages: more efficient (sorts only once, iterates only as much as necessary)
Disadvantages: more code
This gives the results in descending order. You can optionally use array_unshift($top, $x) instead of $top[] = $x; to get it in ascending order, but I think I've read that array_unshift is less efficient because it reindexes the array after each addition, so if optimization is important it would probably be better to just use $top[] = $x; and then iterate the result in reverse order.

Stuck with php foreach loop - need to run the loop with the same key once more

Well I'm stuck with this one.
I have a foreach loop nested in another foreach loop. Now on certain conditions I need to run the outer loop once again with the same key.
While there is this function prev($array), which would set the iterator to the previous position, this does not seem to work and the php docs says
As foreach relies on the >internal array pointer, changing it within the loop may lead to unexpected behavior.
The array I am working on is an associative array, so obviously I cannot easily switch to integer indexed iterations.
$arrLocalCopy = $this->arrPrintOut;
$groupCounter = 0;
foreach($this->arrPrintOut as $kOne => $rowA){
//first and last row contain titles and totals, so we skip them
if($groupCounter < 1 || $groupCounter == sizeof($this->arrPrintOut)-1 ){ $groupCounter++; continue; }
$rowCounter = 0;
foreach($arrLocalCopy as $k => $rowB){
//skip rows that are "before" the outer loops row, as we want to compare only rows below the row we compare to.
if ($rowCounter <= $groupCounter || $rowCounter == sizeof($arrLocalCopy)-1 ) { $rowCounter++; continue; }
//If those values are the same, then values belong to the same group and must be summed together.
if($rowA['t']==$rowB['t'] && $rowA['y']==$rowB['y'] && $rowA['g']==$rowB['g'] && $rowA['q']==$rowB['q'])
{
//if values are the same, then data belongs to one group, lets group them together.
$this->arrPrintOut[$kOne]['s'] += $rowB['s'];
$this->arrPrintOut[$kOne]['b'] += $rowB['b'];
$this->arrPrintOut[$kOne]['v'] += $rowB['v'];
$this->arrPrintOut[$kOne]['r'] += $rowB['r'];
$this->arrPrintOut[$kOne]['k'] += $rowB['k'];
$this->arrPrintOut[$kOne]['n'] += $rowB['n'];
$this->arrPrintOut[$kOne]['m'] += $rowB['m'];
$this->arrPrintOut[$kOne]['l'] += $rowB['l'];
unset($this->arrPrintOut[$k]); //row has been grouped to the current row, so we remove it from the array and from the copy.
unset($arrLocalCopy[$k]);
prev($this->arrPrintOut); //we need to run the outer loop with the same key again, as probably there is another row which could be grouped together with this row.
$groupCounter--;
}
$rowCounter++;
}
$groupCounter++;
}
Here is a code sample.
$rollback is a variable to check if we prev() already
$max is the number of iterations. If you prev() you have to increment it.
<?php
$array = array(1 => "a", 6 => "b", 19 => "c");
$rollback=0;
$max = sizeof($array);
for ($i=0; $i<$max; $i++) {
$key = key($array);
$value = $array[$key];
print "$key => $value\n";
next($array);
if ($value == "b" && $rollback == 0) {
prev($array);
$rollback = 1;
$max++;
}
}

Categories