Sum all values in a 2d array - php

How can I sum all of the values in an array of arrays?
Sample array:
[
[0],
[0],
[1, 6, 0]
]
Desired output: 7

echo array_sum(
call_user_func_array('array_merge', $array)
);
//or
echo array_sum(
array_map('array_sum', $array)
);

Use recursion. This will sum an array to any depth (ignoring php imposed call stack depth limit):
function sum_all($arr)
{
$sum = 0;
foreach ($arr as $val)
{
if (is_array($val))
$sum += sum_all($val);
else
$sum += $val;
}
return $sum;
}

I guess those three arrays are actually all part of a top-level array, like this:
$array = array( array( 0 ), array( 1, 2, 3, 4 ), array( 5, 6 ) );
$sum = 0;
for ( $i = 0; $i < count( $array ); $i++ )
{
$sum += array_sum( $array[$i] );
}

Related

How to duplicate array and sum up those array values in PHP

I have values 1,2,3 and it will create another value like below and produce results by summing. As there are 3 values so that it will create three arrays. if it were 5 values then 5 such arrays are required.
1 2 3
1 2 3
1 2 3
Result is : 1 3 6 5 3
What I am doing is :
$a=[1,2,3];
$b=$a;
$c=$a;
$d=[];
array_push($a,0,0);
array_unshift($b,0);
array_push($b,0);
array_unshift($c,0,0);
$d = array_map(function () {
return array_sum(func_get_args());
}, $a,$b,$c);
print_r($d);
I am not able to find the way to do this for more values than 3 and dynamically. So that I have to just put the values and it gives me the result. I am not asking for the code but you can help me with that how I should approach it. Thanks.
I recommend a linear approach with no preparations or padding or data bloat.
Use the indexes and simple arithmetic to add values to their desired element in the output.
Code: (Demo)
$array = range(1, 3);
$result = [];
foreach ($array as $shifter => $unused) {
foreach ($array as $index => $value) {
$key = $shifter + $index;
$result[$key] = ($result[$key] ?? 0) + $value;
}
}
var_export($result);
// [1, 3, 6, 5, 3]
This is very clean, readable, maintainable, and most efficient.
While $shifter = 0, $key will be 0, 1 then 2; forming [1, 2, 3].
While $shifter = 1, $key will be 1, 2 then 3; forming [1, 3, 5, 3].
While $shifter = 2, $key will be 2, 3 then 4; forming [1, 3, 6, 5, 3].
If you're using PHP 7.4+ you can use the below...
// Ensure the keys are indexed not associative.
$input = array_values( [ 1, 2, 3, 4, 5 ] );
// Create rows.
$rows = [];
foreach ( $input as $key => $value ) {
$rows[ $key ] = array_merge( array_fill( 0, $key, 0 ), $input );
}
// Sum values.
$output = array_map( function() {
return array_sum( func_get_args() );
}, ...$rows );
If you're using a PHP version below 7.4 you can do...
// Ensure the keys are indexed not associative.
$input = array_values( [ 1, 2, 3 ] );
// Create rows.
$rows = [];
foreach ( $input as $key => $value ) {
$rows[ $key ] = array_merge( array_fill( 0, $key, 0 ), $input );
}
// Sum values.
$output = call_user_func_array( 'array_map', array_merge( [ function() {
return array_sum( func_get_args() );
} ], $rows ) );
I'm thinking using an offset makes sense, it's the same as your approach, except I'm adding a 0 using array_fill
$arr = [1,2,3];
$result = [];
$offset = 0;
for($i = 0; $i < count($arr); $i++) {
$result = array_map(function () {
return array_sum(func_get_args());
}, $result, array_merge(array_fill(0, $offset, 0), $arr));
$offset++;
}
var_dump($result);
The advantage of using this is that it uses very little memory even for a very large initial array.

Getting the larger array

$arr1 = [1,2,3];
$arr2 = [1,2,3,4];
$arr3 = [1,2,3,4,5];
echo max( count($arr1), count($arr2), count($arr3) ); // returns 5
with max I do get the count, but don't know which array is larger. How do I get the larger array's reference ($arr3 in this case)?
You should use multidimensional array to store your all arrays then, loop through the multidimensional array and find your largest array,
$lgArraySize = 0; // used for comparing the size of array
$lgArray = array(); // used to store reference of largest array
foreach($arraylist as $array) {
if(count($array) > $largeArraySize) {
$lgArray = &$array;
$lgArraySize = count($array);
}
}
print_r($largeArray);
Here is the one liner code.(that you want)
function findMax( $row ){
return count($row);
}
$maxArrayPos = array_search(max( array_map("findMax", $multi )), array_map("findMax", $multi ));
print_r($multi[$maxArrayPos]);
You can make this as one liner.
It's better to use indexes for such arrays, so that traversal is easy.
You can get reference to the larger array as follows -
$arr;
$arr[0] = [6,2,1,12,32,11];
$arr[1] = [1,2,3,4];
$arr[2] = [1,2,3,4,4,4,1,1,1,1];
$count = -1;//will hold max count
$big;//larger array will be stored here
foreach ($arr as $key => $value) {
$curCount = count($arr[$key]);//curCount holds current array size
//checking for biggest array count
if($curCount>$count){
$count = $curCount;
$big = &$arr[$key];
}
}
var_dump($big);
Maybe the logic is too bulky though!
Is this what you need?
$arr1 = [1,2,3];
$arr2 = [1,2,3,4];
$arr3 = [1,2,3,4,5];
$countmax = [
"arr1" => $arr1,
"arr2" => $arr2,
"arr3" => $arr3,
];
$value = max($countmax);
print_r($value);
Oputput:
Array ( [0] => 1 [1] => 2 [2] => 3 [3] => 4 [4] => 5 )
Try this :
function laregr(){
$numargs = func_num_args();
$large_array = array();
if($numargs){
$arg_list = func_get_args();
$max = 0;
foreach($arg_list as $arg){
if(is_array($arg)){
$big = count($arg);
if($big >= $max ){
$max = $big;
$large_array = $arg;
}
}
}
}
return $large_array;
}
$arr1 = [1,2,3,4,5,6];
$arr2 = [1,2,3,4,5];
$arr4 = [1,2,3,4, 5,6,7];
$max_array = laregr($arr1, $arr2, $arr4); // large($arr1, ...)
print_r($max_array);
You may like to store all arrays inside an array and would like to do a foreach I think.
function max_length($array) {
$max = 0;
foreach($array as $child) {
if(count($child) > $max) {
$max = count($child);
}
}
return $max;
}
check this why it is better approach .

How to count the consecutive duplicate values in an array?

I have an array like this:
$arr = array(1, 1, 1, 2, 2, 3, 3, 1, 1, 2, 2, 3);
I found the function array_count_values(), but it will group all of the same values and count the occurrences without respecting breaks in the consecutive sequences.
$result[1] = 5
$result[2] = 4
$result[3] = 3
How can I group each set of consecutive values and count the length of each sequence? Notice there are two sets of sequences for the numbers 1, 2, and 3.
The data that I expect to generate needs to resemble this:
[1] = 3;
[2] = 2;
[3] = 2;
[1] = 2;
[2] = 2;
[3] = 1;
It can be done simply manually:
$arr = array(1,1,1,2,2,3,3,1,1,2,2,3);
$result = array();
$prev_value = array('value' => null, 'amount' => null);
foreach ($arr as $val) {
if ($prev_value['value'] != $val) {
unset($prev_value);
$prev_value = array('value' => $val, 'amount' => 0);
$result[] =& $prev_value;
}
$prev_value['amount']++;
}
var_dump($result);
My suggestion is to extract&remove the first value from the array prior to entering the loop and use a temporary array ($carry) to track whether each new value matches the key in the carry array. If so, increment it. If not, push the completed sequence count into the result array and overwrite the carry with the new value and set the counter to 1. When the loop finishes, push the lingering carry into the result set. My snippet does not check if the input array is empty; if necessary, add that condition to your project.
Code: (Demo)
$array = [1,1,1,2,2,3,3,1,1,2,2,3];
$result = [];
$carry = [array_shift($array) => 1];
foreach ($array as $value) {
if (isset($carry[$value])) {
++$carry[$value];
} else {
$result[] = $carry;
$carry = [$value => 1];
}
}
$result[] = $carry;
print_r($result);
Output: (condensed to reduce page bloat)
[
[1 => 3],
[2 => 2],
[3 => 2],
[1 => 2],
[2 => 2],
[3 => 1],
]
If you'd rather implement a zerkms-style, modify-by-reference style technique, the following snippet provides the same result as the above snippet.
Effectively, it pushes every newly encountered value as an associative, single-element array into the indexed result array. Because the pushed subarray is declared as a variable ($carry) then assigned-by-reference (= &) to the result array, incrementation of $carry will be applied to the deeply nested value in the result array. The output array requires the additional depth in its structure so that a given value which occurs multiple times can be reliably stored.
Code: (Demo)
$result = [];
$carry = [];
foreach ($array as $value) {
if ($carry && key($carry) === $value) {
++$carry[$value];
} else {
unset($carry);
$carry = [$value => 1];
$result[] = &$carry;
}
}
unset($carry);
print_r($result);
Unsetting the reference variable $carry after the loop may not be necessary, but if there is any potential re-use of that variable within the variable's scope, it will be important to uncouple the reference with unset().
And just for fun, here is a hideous regex-infused approach that works with the sample data: Demo
What about PHP's array_count_values function?
<?php
$array = array(1, "hello", 1, "world", "hello");
print_r(array_count_values($array));
?>
output:
Array
(
[1] => 2
[hello] => 2
[world] => 1
)
function findRepetitions($times, $array) {
$values = array_unique($array);
$counts = [];
foreach($values as $value) {
$counts[] = ['value' => $value, 'count' => 0];
}
foreach ($array as $value) {
foreach ($counts as $key => $count) {
if ($count['value'] === $value) {
$counts[$key]['count']++;
}
}
}
$repetitions = [];
foreach ($counts as $count) {
if ($count['count'] === $times) {
$repetitions[] = $count['value'];
}
}
return $repetitions;
}
$current = null;
foreach($your_array as $v) {
if($v == $current) {
$result[count($result)-1]++;
} else {
$result[] = 1;
$current = $v;
}
}
var_dump($result);
Here is the way that I would do it:
function SplitIntoGroups($array)
{
$toReturnArray = array();
$currentNumber = $array[0];
$currentCount = 1;
for($i=1; $i <= count($array); $i++)
{
if($array[$i] == $currentNumber)
{
$currentCount++;
}
else
{
$toReturnArray[] = array($currentNumber, $currentCount);
$currentNumber = $array[$i];
$currentCount = 1;
}
}
return $toReturnArray;
}
$answer = SplitIntoGroups(array(1,1,1,2,2,3,3,1,1,2,2,3));
for($i=0; $i<count($answer); $i++)
{
echo '[' . $answer[$i][0] . '] = ' . $answer[$i][1] . '<br />';
}

How to find the duplicate and highest value in an array

I have an array like this
array={'a'=>'2','b'=>'5', 'c'=>'6', 'd'=>'6', 'e'=>'2'};
The array value might be different depending on the $_POST variables. My question is how to find the highest value in my array and return the index key. In my case, I need to get 'c' and 'd' and the value of 6. Not sure how to do this. Any helps would be appreciated. Thanks.
$max = max(array_values($array));
$keys = array_keys($array, $max);
Have a look at arsort which will sort an array in reverse order and maintain index association. So:
arsort($array);
This will end up with the largest values at the top of the array. Depending on what you need array_unique can remove duplicate values from your array.
$array = array(
'key1' => 22,
'key2' => 17,
'key3' => 19,
'key4' => 21,
'key5' => 24,
'key6' => 8,
);
function getHighest($array)
{
$highest = 0;
foreach($array as $index => $value)
{
if(is_numeric($value) && $value > $highest)
{
$highest = $index;
}
}
return $highest;
}
echo getHighest($array); //key5
Or this should do the magic, it would probably be faster than php built-in functions
$maxValue = -1;
$max = array();
foreach ($items as $key => $item) {
if ($item == $maxValue) {
$max[] = $key;
} elseif ($item > $maxValue) {
$max = array();
$max[] = $key;
$maxValue = $item;
}
}

Smoothing Data Array PHP

I have an array:
Array
(
[1] => 25
[2] => 50
[3] => 25
)
I would like to make it into:
Array
(
[1] => 50
[2] => 50
)
To do this I split the middle value between 1 and 3. This is the simplest example, where the split is 50,50. I would like to be able to take a 15 element array down to 6 elements.
Any ideas?
Additional Examples
[10, 15, 20, 25] Reduced to two elements: 25(10 + 15),45(20 + 25)
[10, 10, 10, 10, 11] Reduced to two elements: 25(10 + 10 + (10/2)),26((10/2) + 10 + 11)
After doing additional tests on Peter's solution, I noticed it did not get me what I expected if the reduce to size is an odd number. Here is the function I came up with. It also inflates data sets that are smaller then the requested size.
<?php
function reduceto($data,$r) {
$c = count($data);
// just enough data
if ($c == $r) return $data;
// not enough data
if ($r > $c) {
$x = ceil($r/$c);
$temp = array();
foreach ($data as $v) for($i = 0; $i < $x; $i++) $temp[] = $v;
$data = $temp;
$c = count($data);
}
// more data then needed
if ($c > $r) {
$temp = array();
foreach ($data as $v) for($i = 0; $i < $r; $i++) $temp[] = $v;
$data = array_map('array_sum',array_chunk($temp,$c));
}
foreach ($data as $k => $v) $data[$k] = $v / $r;
return $data;
}
?>
You could sum the values using array_sum() and then, depending on the number of elements you want to have in your resulting array, divide that sum and fill every element you want to keep with the result of your division.
(Here I'm assuming you'll use a second array, but you could unset the unneeded if you prefer it that way).
Here's my stab at your issue
<pre>
<?php
class Thingy
{
protected $store;
protected $universe;
public function __construct( array $data )
{
$this->store = $data;
$this->universe = array_sum( $data );
}
public function reduceTo( $size )
{
// Guard condition incase reduction size is too big
$storeSize = count( $this->store );
if ( $size >= $storeSize )
{
return $this->store;
}
// Odd number of elements must be handled differently
if ( $storeSize & 1 )
{
$chunked = array_chunk( $this->store, ceil( $storeSize / 2 ) );
$middleValue = array_pop( $chunked[0] );
$chunked = array_chunk( array_merge( $chunked[0], $chunked[1] ), floor( $storeSize / $size ) );
// Distribute odd-man-out amonst other values
foreach ( $chunked as &$chunk )
{
$chunk[] = $middleValue / $size;
}
} else {
$chunked = array_chunk( $this->store, floor( $storeSize / $size ) );
}
return array_map( 'array_sum', $chunked );
}
}
$tests = array(
array( 2, array( 25, 50, 25 ) )
, array( 2, array( 10, 15, 20, 25 ) )
, array( 2, array( 10, 10, 10, 10, 11 ) )
, array( 6, array_fill( 0, 15, 1 ) )
);
foreach( $tests as $test )
{
$t = new Thingy( $test[1] );
print_r( $t->reduceTo( $test[0] ) );
}
?>
</pre>

Categories