Related
How can I add all the columnar values by associative key? Note that key sets are dynamic.
Input array:
Array
(
[0] => Array
(
[gozhi] => 2
[uzorong] => 1
[ngangla] => 4
[langthel] => 5
)
[1] => Array
(
[gozhi] => 5
[uzorong] => 0
[ngangla] => 3
[langthel] => 2
)
[2] => Array
(
[gozhi] => 3
[uzorong] => 0
[ngangla] => 1
[langthel] => 3
)
)
Desired result:
Array
(
[gozhi] => 10
[uzorong] => 1
[ngangla] => 8
[langthel] => 10
)
You can use array_walk_recursive() to get a general-case solution for your problem (the one when each inner array can possibly have unique keys).
$final = array();
array_walk_recursive($input, function($item, $key) use (&$final){
$final[$key] = isset($final[$key]) ? $item + $final[$key] : $item;
});
Example with array_walk_recursive() for the general case
Also, since PHP 5.5 you can use the array_column() function to achieve the result you want for the exact key, [gozhi], for example :
array_sum(array_column($input, 'gozhi'));
Example with array_column() for the specified key
If you want to get the total sum of all inner arrays with the same keys (the desired result that you've posted), you can do something like this (bearing in mind that the first inner array must have the same structure as the others) :
$final = array_shift($input);
foreach ($final as $key => &$value){
$value += array_sum(array_column($input, $key));
}
unset($value);
Example with array_column() in case all inner arrays have the same keys
If you want a general-case solution using array_column() then at first you may consider to get all unique keys , and then get the sum for each key :
$final = array();
foreach($input as $value)
$final = array_merge($final, $value);
foreach($final as $key => &$value)
$value = array_sum(array_column($input, $key));
unset($value);
Example with array_column() for the general case
$sumArray = array();
foreach ($myArray as $k=>$subArray) {
foreach ($subArray as $id=>$value) {
isset($sumArray[$id]) || $sumArray[$id] = 0;
$sumArray[$id]+=$value;
}
}
print_r($sumArray);
Use this snippet:
$key = 'gozhi';
$sum = array_sum(array_column($array,$key));
Here is a solution similar to the two others:
$acc = array_shift($arr);
foreach ($arr as $val) {
foreach ($val as $key => $val) {
$acc[$key] += $val;
}
}
But this doesn’t need to check if the array keys already exist and doesn’t throw notices neither.
It can also be done using array_map :
$rArray = array(
0 => array(
'gozhi' => 2,
'uzorong' => 1,
'ngangla' => 4,
'langthel' => 5
),
1 => array(
'gozhi' => 5,
'uzorong' => 0,
'ngangla' => 3,
'langthel' => 2
),
2 => array(
'gozhi' => 3,
'uzorong' => 0,
'ngangla' => 1,
'langthel' => 3
),
);
$sumResult = call_user_func_array('array_map', array_merge(['sum'], $rArray));
function sum()
{
return array_sum(func_get_args());
}
$newarr=array();
foreach($arrs as $value)
{
foreach($value as $key=>$secondValue)
{
if(!isset($newarr[$key]))
{
$newarr[$key]=0;
}
$newarr[$key]+=$secondValue;
}
}
Another version, with some benefits below.
$sum = ArrayHelper::copyKeys($arr[0]);
foreach ($arr as $item) {
ArrayHelper::addArrays($sum, $item);
}
class ArrayHelper {
public function addArrays(Array &$to, Array $from) {
foreach ($from as $key=>$value) {
$to[$key] += $value;
}
}
public function copyKeys(Array $from, $init=0) {
return array_fill_keys(array_keys($from), $init);
}
}
I wanted to combine the best of Gumbo's, Graviton's, and Chris J's answer with the following goals so I could use this in my app:
a) Initialize the 'sum' array keys outside of the loop (Gumbo). Should help with performance on very large arrays (not tested yet!). Eliminates notices.
b) Main logic is easy to understand without hitting the manuals. (Graviton, Chris J).
c) Solve the more general problem of adding the values of any two arrays with the same keys and make it less dependent on the sub-array structure.
Unlike Gumbo's solution, you could reuse this in cases where the values are not in sub arrays. Imagine in the example below that $arr1 and $arr2 are not hard-coded, but are being returned as the result of calling a function inside a loop.
$arr1 = array(
'gozhi' => 2,
'uzorong' => 1,
'ngangla' => 4,
'langthel' => 5
);
$arr2 = array(
'gozhi' => 5,
'uzorong' => 0,
'ngangla' => 3,
'langthel' => 2
);
$sum = ArrayHelper::copyKeys($arr1);
ArrayHelper::addArrays($sum, $arr1);
ArrayHelper::addArrays($sum, $arr2);
It can also be done using array_walk:
function array_sum_values(array $input, $key) {
$sum = 0;
array_walk($input, function($item, $index, $params) {
if (!empty($item[$params[1]]))
$params[0] += $item[$params[1]];
}, array(&$sum, $key)
);
return $sum;
}
var_dump(array_sum_values($arr, 'gozhi'));
Not so readable like previous solutions but it works :)
Go through each item of the array and sum values to previous values if they exist, if not just assign the value.
<?php
$array =
[
[
'a'=>1,
'b'=>1,
'c'=>1,
],
[
'a'=>2,
'b'=>2,
],
[
'a'=>3,
'd'=>3,
]
];
$result = array_reduce($array, function($carry, $item) {
foreach($item as $k => $v)
$carry[$k] = $v + ($carry[$k] ?? 0);
return $carry;
}, []);
print_r($result);
Output:
Array
(
[a] => 6
[b] => 3
[c] => 1
[d] => 3
)
Or just loop through each sub array, and group the values for each column. Eventually summing them:
foreach($array as $subarray)
foreach($subarray as $key => $value)
$grouped[$key][] = $value;
$sums = array_map('array_sum', $grouped);
Here's a version where the array keys may not be the same for both arrays, but you want them all to be there in the final array.
function array_add_by_key( $array1, $array2 ) {
foreach ( $array2 as $k => $a ) {
if ( array_key_exists( $k, $array1 ) ) {
$array1[$k] += $a;
} else {
$array1[$k] = $a;
}
}
return $array1;
}
We need to check first if array key does exist.
CODE:
$sum = array();
foreach ($array as $key => $sub_array) {
foreach ($sub_array as $sub_key => $value) {
//If array key doesn't exists then create and initize first before we add a value.
//Without this we will have an Undefined index error.
if( ! array_key_exists($sub_key, $sum)) $sum[$sub_key] = 0;
//Add Value
$sum[$sub_key]+=$value;
}
}
print_r($sum);
OUTPUT With Array Key Validation:
Array
(
[gozhi] => 10
[uzorong] => 1
[ngangla] => 8
[langthel] => 10
)
OUTPUT Without Array Key Validation:
Notice: Undefined index: gozhi in F:\web\index.php on line 37
Notice: Undefined index: uzorong in F:\web\index.php on line 37
Notice: Undefined index: ngangla in F:\web\index.php on line 37
Notice: Undefined index: langthel in F:\web\index.php on line 37
Array
(
[gozhi] => 10
[uzorong] => 1
[ngangla] => 8
[langthel] => 10
)
This is a bad practice although it prints the output. Always check first if key does exist.
Simple example with array_reduce()
$numbers = [10,20,30];
$total = 0;
foreach ($numbers as $number) {
$total += $number;
}
echo $total; // 60
=>
$numbers = [10,20,30];
$total = array_reduce($numbers, function ($previous, $current) {
return $previous + $current;
});
echo $total; // 60
With assoc array:
$carts = [
['item' => 'A', 'qty' => 2, 'price' => 10],
['item' => 'B', 'qty' => 3, 'price' => 20],
['item' => 'C', 'qty' => 5, 'price' => 30]
];
$total = array_reduce(
$carts,
function ($prev, $item) {
return $prev + $item['qty'] * $item['price'];
}
);
echo $total; // 155
More info => https://www.phptutorial.net/php-tutorial/php-array_reduce/
For those who landed here and are searching for a solution that merges N arrays AND also sums the values of identical keys found in the N arrays, I've written this function that works recursively as well. (See: https://gist.github.com/Nickology/f700e319cbafab5eaedc)
Example:
$a = array( "A" => "bob", "sum" => 10, "C" => array("x","y","z" => 50) );
$b = array( "A" => "max", "sum" => 12, "C" => array("x","y","z" => 45) );
$c = array( "A" => "tom", "sum" => 8, "C" => array("x","y","z" => 50, "w" => 1) );
print_r(array_merge_recursive_numeric($a,$b,$c));
Will result in:
Array
(
[A] => tom
[sum] => 30
[C] => Array
(
[0] => x
[1] => y
[z] => 145
[w] => 1
)
)
Here's the code:
<?php
/**
* array_merge_recursive_numeric function. Merges N arrays into one array AND sums the values of identical keys.
* WARNING: If keys have values of different types, the latter values replace the previous ones.
*
* Source: https://gist.github.com/Nickology/f700e319cbafab5eaedc
* #params N arrays (all parameters must be arrays)
* #author Nick Jouannem <nick#nickology.com>
* #access public
* #return void
*/
function array_merge_recursive_numeric() {
// Gather all arrays
$arrays = func_get_args();
// If there's only one array, it's already merged
if (count($arrays)==1) {
return $arrays[0];
}
// Remove any items in $arrays that are NOT arrays
foreach($arrays as $key => $array) {
if (!is_array($array)) {
unset($arrays[$key]);
}
}
// We start by setting the first array as our final array.
// We will merge all other arrays with this one.
$final = array_shift($arrays);
foreach($arrays as $b) {
foreach($final as $key => $value) {
// If $key does not exist in $b, then it is unique and can be safely merged
if (!isset($b[$key])) {
$final[$key] = $value;
} else {
// If $key is present in $b, then we need to merge and sum numeric values in both
if ( is_numeric($value) && is_numeric($b[$key]) ) {
// If both values for these keys are numeric, we sum them
$final[$key] = $value + $b[$key];
} else if (is_array($value) && is_array($b[$key])) {
// If both values are arrays, we recursively call ourself
$final[$key] = array_merge_recursive_numeric($value, $b[$key]);
} else {
// If both keys exist but differ in type, then we cannot merge them.
// In this scenario, we will $b's value for $key is used
$final[$key] = $b[$key];
}
}
}
// Finally, we need to merge any keys that exist only in $b
foreach($b as $key => $value) {
if (!isset($final[$key])) {
$final[$key] = $value;
}
}
}
return $final;
}
?>
Here you have how I usually do this kind of operations.
// We declare an empty array in wich we will store the results
$sumArray = array();
// We loop through all the key-value pairs in $myArray
foreach ($myArray as $k=>$subArray) {
// Each value is an array, we loop through it
foreach ($subArray as $id=>$value) {
// If $sumArray has not $id as key we initialize it to zero
if(!isset($sumArray[$id])){
$sumArray[$id] = 0;
}
// If the array already has a key named $id, we increment its value
$sumArray[$id]+=$value;
}
}
print_r($sumArray);
You can try this:
$c = array_map(function () {
return array_sum(func_get_args());
},$a, $b);
and finally:
print_r($c);
this works great on my laravel project
print_r($Array); // your original array
$_SUM = [];
// count($Array[0]) => if the number of keys are equall in all arrays then do a count of index 0 etc.
for ($i=0; $i < count($Array[0]); $i++) {
$_SUM[] = $Array[0][$i] + $Array[1][$i]; // do a for loop on the count
}
print_r($_SUM); // get a sumed up array
$sumArray = array();
foreach ($myArray as $k => $subArray) {
foreach ($subArray as $id => $value) {
if (!isset($sumArray[$id])) {
$sumArray[$id] = 0;
}
$sumArray[$id]+=$value;
}
}
$sumArray = array();
foreach ($myArray as $k=>$subArray) {
foreach ($subArray as $id=>$value) {
if(!isset($sumArray[$id])){
$sumArray[$id] =$value;
}else {
$sumArray[$id]+=$value;
}
}
}
print_r($sumArray);
`
For example, you can pluck all fields from a result like this below.
I am picking out the 'balance' from an array and save to a variable
$kii = $user->pluck('balance');
then on the next line u can sum like this:
$sum = $kii->sum();
Hope it helps.
Code here:
$temp_arr = [];
foreach ($a as $k => $v) {
if(!is_null($v)) {
$sum = isset($temp_arr[$v[0]]) ? ((int)$v[5] + $sum) : (int)$v[5];
$temp_arr[$v[0]] = $sum;
}
}
return $temp_arr;
Result:
{SEQ_OK: 1328,SEQ_ERROR: 561}
Lets say I have this array:
$array = array('a'=>1,'z'=>2,'d'=>4);
Later in the script, I want to add the value 'c'=>3 before 'z'. How can I do this?
Yes, the order is important. When I run a foreach() through the array, I do NOT want this newly added value added to the end of the array. I am getting this array from a mysql_fetch_assoc()
The keys I used above are placeholders. Using ksort() will not achieve what I want.
http://www.php.net/manual/en/function.array-splice.php#88896 accomplishes what I'm looking for but I'm looking for something simpler.
Take a sample db table with about 30 columns. I get this data using mysql_fetch_assoc(). In this new array, after column 'pizza' and 'drink', I want to add a new column 'full_dinner' that combines the values of 'pizza' and 'drink' so that when I run a foreach() on the said array, 'full_dinner' comes directly after 'drink'
Am I missing something?
$key = 'z';
$offset = array_search($key, array_keys($array));
$result = array_merge
(
array_slice($array, 0, $offset),
array('c' => 3),
array_slice($array, $offset, null)
);
Handling of nonexistent keys (appending $data by default):
function insertBeforeKey($array, $key, $data = null)
{
if (($offset = array_search($key, array_keys($array))) === false) // if the key doesn't exist
{
$offset = 0; // should we prepend $array with $data?
$offset = count($array); // or should we append $array with $data? lets pick this one...
}
return array_merge(array_slice($array, 0, $offset), (array) $data, array_slice($array, $offset));
}
Demo:
$array = array('a' => 1, 'z' => 2, 'd' => 4);
// array(4) { ["a"]=> int(1) ["c"]=> int(3) ["z"]=> int(2) ["d"]=> int(4) }
var_dump(insertBeforeKey($array, 'z', array('c' => 3)));
// array(4) { ["a"]=> int(1) ["z"]=> int(2) ["d"]=> int(4) ["c"]=> int(3) }
var_dump(insertBeforeKey($array, 'y', array('c' => 3)));
A simple approach to this is to iterate through the original array, constructing a new one as you go:
function InsertBeforeKey( $originalArray, $originalKey, $insertKey, $insertValue ) {
$newArray = array();
$inserted = false;
foreach( $originalArray as $key => $value ) {
if( !$inserted && $key === $originalKey ) {
$newArray[ $insertKey ] = $insertValue;
$inserted = true;
}
$newArray[ $key ] = $value;
}
return $newArray;
}
Then simply call
$array = InsertBeforeKey( $array, 'd', 'c', 3 );
According to your original question the best answer I can find is this:
$a = array('a'=>1,'z'=>2,'d'=>4);
$splitIndex = array_search('z', array_keys($a));
$b = array_merge(
array_slice($a, 0, $splitIndex),
array('c' => 3),
array_slice($a, $splitIndex)
);
var_dump($b);
array(4) {
["a"]=>
int(1)
["c"]=>
int(3)
["z"]=>
int(2)
["d"]=>
int(4)
}
Depending on how big your arrays are you will duplicate quite some data in internal memory, regardless if you use this solution or another.
Furthermore your fifth edit seems to indicate that alternatively your SQL query could be improved. What you seem to want to do there would be something like this:
SELECT a, b, CONCAT(a, ' ', b) AS ab FROM ... WHERE ...
If changing your SELECT statement could make the PHP solution redundant, you should definitely go with the modified SQL.
function insertValue($oldArray, $newKey, $newValue, $followingKey) {
$newArray = array ();
foreach (array_keys($oldArray) as $k) {
if ($k == $followingKey)
$newArray[$newKey] = $newValue;
$newArray[$k] = $oldArray [$k];
}
return $newArray;
}
You call it as
insertValue($array, 'c', '3', 'z')
As for Edit 5:
edit your sql, so that it reads
SELECT ..., pizza, drink, pizza+drink as full_meal, ... FROM ....
and you have the column automatically:
Array (
...
'pizza' => 12,
'drink' => 5,
'full_meal' => 17,
...
)
Associative arrays are not ordered, so you can simply add with $array['c'] = 3.
If order is important, one option is switch to a data structure more like:
$array = array(
array('a' => 1),
array('b' => 2)
array('d' => 4)
);
Then, use array_splice($array, 2, 0, array('c' => 3)) to insert at position 2. See manual on array_splice.
An alternative approach is to supplement the associative array structure with an ordered index that determines the iterative order of keys. For instance:
$index = array('a','b','d');
// Add new value and update index
$array['c'] = 3;
array_splice($index, 2, 0, 'c');
// Iterate the array in order
foreach $index as $key {
$value = $array[$key];
}
You can define your own sortmap when doing a bubble-sort by key. It's probably not terribly efficient but it works.
<pre>
<?php
$array = array('a'=>1,'z'=>2,'d'=>4);
$array['c'] = 3;
print_r( $array );
uksort( $array, 'sorter' );
print_r( $array );
function sorter( $a, $b )
{
static $ordinality = array(
'a' => 1
, 'c' => 2
, 'z' => 3
, 'd' => 4
);
return $ordinality[$a] - $ordinality[$b];
}
?>
</pre>
Here's an approach based on ArrayObject using this same concept
$array = new CitizenArray( array('a'=>1,'z'=>2,'d'=>4) );
$array['c'] = 3;
foreach ( $array as $key => $value )
{
echo "$key: $value <br>";
}
class CitizenArray extends ArrayObject
{
static protected $ordinality = array(
'a' => 1
, 'c' => 2
, 'z' => 3
, 'd' => 4
);
function offsetSet( $key, $value )
{
parent::offsetSet( $key, $value );
$this->uksort( array( $this, 'sorter' ) );
}
function sorter( $a, $b )
{
return self::$ordinality[$a] - self::$ordinality[$b];
}
}
For the moment the best i can found to try to minimize the creation of new arrays are these two functions :
the first one try to replace value into the original array and the second one return a new array.
// replace value into the original array
function insert_key_before_inplace(&$base, $beforeKey, $newKey, $value) {
$index = 0;
foreach($base as $key => $val) {
if ($key==$beforeKey) break;
$index++;
}
$end = array_splice($base, $index, count($base)-$index);
$base[$newKey] = $value;
foreach($end as $key => $val) $base[$key] = $val;
}
$array = array('a'=>1,'z'=>2,'d'=>4);
insert_key_before_inplace($array, 'z', 'c', 3);
var_export($array); // array ( 'a' => 1, 'c' => 3, 'z' => 2, 'd' => 4, )
// create new array
function insert_key_before($base, $beforeKey, $newKey, $value) {
$index = 0;
foreach($base as $key => $val) {
if ($key==$beforeKey) break;
$index++;
}
$end = array_splice($base, $index, count($base)-$index);
$base[$newKey] = $value;
return $base+$end;
}
$array = array('a'=>1,'z'=>2,'d'=>4);
$newArray=insert_key_before($array, 'z', 'c', 3);
var_export($array); // ( 'a' => 1, 'z' => 2, 'd' => 4, )
var_export($newArray); // array ( 'a' => 1, 'c' => 3, 'z' => 2, 'd' => 4, )
function putarrayelement(&$array, $arrayobject, $elementposition, $value = null) {
$count = 0;
$return = array();
foreach ($array as $k => $v) {
if ($count == $elementposition) {
if (!$value) {
$value = $count;
}
$return[$value] = $arrayobject;
$inserted = true;
}
$return[$k] = $v;
$count++;
}
if (!$value) {
$value = $count;
}
if (!$inserted){
$return[$value];
}
$array = $return;
return $array;
}
$array = array('a' => 1, 'z' => 2, 'd' => 4);
putarrayelement($array, '3', 1, 'c');
print_r($array);
Great usage of array functions but how about this as a simpler way:
Add a static column to the SQL and then replace it in the resultant array. Order stays the same:
SQL :
Select pizza , drink , 'pizza-drink' as 'pizza-drink' , 28 columns..... From Table
Array :
$result['pizza-drink'] = $result['pizza'] . $result['drink'];
A simplified Alix Axel function if you need to just insert data in nth position:
function array_middle_push( array $array, int $position, array $data ): array {
return array_merge( array_slice( $array, 0, $position ), $data, array_slice( $array, $position ) );
}
Try this
$array['c']=3;
An associative array is not ordered by default, but if you wanted to sort them alphabetically you could use ksort() to sort the array by it's key.
If you check out the PHP article for ksort() you will se it's easy to sort an array by its key, for example:
<?php
$fruits = array("d"=>"lemon", "a"=>"orange", "b"=>"banana", "c"=>"apple");
ksort($fruits);
foreach ($fruits as $key => $val) {
echo "$key = $val\n";
}
?>
// The above example will output:
a = orange
b = banana
c = apple
d = lemon
you can add it by doing
$array['c']=3;
and if you absolutely want it sorted for printing purposes, you can use php's ksort($array) function
if the keys are not sortable by ksort, then you will have to create your own sort by using php's uasort function. see examples here
http://php.net/manual/en/function.uasort.php
Here is my precode...
$keys = array('a', 'b', 'c', 'd');
$number = 10;
And here is my code...
eval('$array[\''.implode('\'][\'',$keys).'\'] = $number;');
Using this, I get the following result...
Array (
[a] => Array
(
[b] => Array
(
[c] => Array
(
[d] => 10
)
)
)
)
Now, the problem is that this is the exact result I want, but I don't want to use eval().
As input to my code, I have a list of keys and a number. The number should be set to the value of the keys array being used to generate child-based keys for a certain array $array.
Is there a different way that I can achieve this? I don't want to overwrite the keys/numbers with new values as the code works - eval() preserves this already, so my new code should do the same.
Please note that the code below (which you evaluate) will generate a warning, and will therefore not work on projects with error reporting up to the max:
$array = array();
$array['a']['b'] = 42; // $array['a'] is not an array... yet
Since you're using PHP 5, you can work with references to manipulate your array while traversing the branch of your tree that you wish to modify.
$current = & $array;
foreach ($keys as $key):
if (!isset($current[$key]) || !is_array($current[$key]))
$current[$key] = array();
$current = & $current[$key];
endforeach;
$current = $value;
Edit: corrected for avoiding warnings and conflicts.
Here is a full code example showing how it would work. Whats important is that you use a reference to the array so you can modify it:
<?php
$keys = array('a', 'b', 'c', 'd');
$number = 10;
$org_array = array(
"a" => "string",
"z" => array( "k" => false)
);
function write_to_array(&$array, $keys, $number){
$key = array_shift($keys);
if(!is_array($array[$key])) $array[$key] = array();
if(!empty($keys)){
write_to_array($array[$key], $keys, $number);
} else {
$array[$key] = $number;
}
}
write_to_array($org_array, $keys, $number);
print_r($org_array);
?>
function deepmagic($levels, $value)
{
if(count($levels) > 0)
{
return array($levels[0] => deepmagic(array_slice($levels, 1),
$value));
}
else
{
return $value;
}
}
$a = deepmagic(Array('a', 'b', 'c', 'd'), 10);
var_dump($a);
Output:
array(1) {
["a"]=>
array(1) {
["b"]=>
array(1) {
["c"]=>
array(1) {
["d"]=>
int(10)
}
}
}
}
How can I add all the columnar values by associative key? Note that key sets are dynamic.
Input array:
Array
(
[0] => Array
(
[gozhi] => 2
[uzorong] => 1
[ngangla] => 4
[langthel] => 5
)
[1] => Array
(
[gozhi] => 5
[uzorong] => 0
[ngangla] => 3
[langthel] => 2
)
[2] => Array
(
[gozhi] => 3
[uzorong] => 0
[ngangla] => 1
[langthel] => 3
)
)
Desired result:
Array
(
[gozhi] => 10
[uzorong] => 1
[ngangla] => 8
[langthel] => 10
)
You can use array_walk_recursive() to get a general-case solution for your problem (the one when each inner array can possibly have unique keys).
$final = array();
array_walk_recursive($input, function($item, $key) use (&$final){
$final[$key] = isset($final[$key]) ? $item + $final[$key] : $item;
});
Example with array_walk_recursive() for the general case
Also, since PHP 5.5 you can use the array_column() function to achieve the result you want for the exact key, [gozhi], for example :
array_sum(array_column($input, 'gozhi'));
Example with array_column() for the specified key
If you want to get the total sum of all inner arrays with the same keys (the desired result that you've posted), you can do something like this (bearing in mind that the first inner array must have the same structure as the others) :
$final = array_shift($input);
foreach ($final as $key => &$value){
$value += array_sum(array_column($input, $key));
}
unset($value);
Example with array_column() in case all inner arrays have the same keys
If you want a general-case solution using array_column() then at first you may consider to get all unique keys , and then get the sum for each key :
$final = array();
foreach($input as $value)
$final = array_merge($final, $value);
foreach($final as $key => &$value)
$value = array_sum(array_column($input, $key));
unset($value);
Example with array_column() for the general case
$sumArray = array();
foreach ($myArray as $k=>$subArray) {
foreach ($subArray as $id=>$value) {
isset($sumArray[$id]) || $sumArray[$id] = 0;
$sumArray[$id]+=$value;
}
}
print_r($sumArray);
Use this snippet:
$key = 'gozhi';
$sum = array_sum(array_column($array,$key));
Here is a solution similar to the two others:
$acc = array_shift($arr);
foreach ($arr as $val) {
foreach ($val as $key => $val) {
$acc[$key] += $val;
}
}
But this doesn’t need to check if the array keys already exist and doesn’t throw notices neither.
It can also be done using array_map :
$rArray = array(
0 => array(
'gozhi' => 2,
'uzorong' => 1,
'ngangla' => 4,
'langthel' => 5
),
1 => array(
'gozhi' => 5,
'uzorong' => 0,
'ngangla' => 3,
'langthel' => 2
),
2 => array(
'gozhi' => 3,
'uzorong' => 0,
'ngangla' => 1,
'langthel' => 3
),
);
$sumResult = call_user_func_array('array_map', array_merge(['sum'], $rArray));
function sum()
{
return array_sum(func_get_args());
}
$newarr=array();
foreach($arrs as $value)
{
foreach($value as $key=>$secondValue)
{
if(!isset($newarr[$key]))
{
$newarr[$key]=0;
}
$newarr[$key]+=$secondValue;
}
}
Another version, with some benefits below.
$sum = ArrayHelper::copyKeys($arr[0]);
foreach ($arr as $item) {
ArrayHelper::addArrays($sum, $item);
}
class ArrayHelper {
public function addArrays(Array &$to, Array $from) {
foreach ($from as $key=>$value) {
$to[$key] += $value;
}
}
public function copyKeys(Array $from, $init=0) {
return array_fill_keys(array_keys($from), $init);
}
}
I wanted to combine the best of Gumbo's, Graviton's, and Chris J's answer with the following goals so I could use this in my app:
a) Initialize the 'sum' array keys outside of the loop (Gumbo). Should help with performance on very large arrays (not tested yet!). Eliminates notices.
b) Main logic is easy to understand without hitting the manuals. (Graviton, Chris J).
c) Solve the more general problem of adding the values of any two arrays with the same keys and make it less dependent on the sub-array structure.
Unlike Gumbo's solution, you could reuse this in cases where the values are not in sub arrays. Imagine in the example below that $arr1 and $arr2 are not hard-coded, but are being returned as the result of calling a function inside a loop.
$arr1 = array(
'gozhi' => 2,
'uzorong' => 1,
'ngangla' => 4,
'langthel' => 5
);
$arr2 = array(
'gozhi' => 5,
'uzorong' => 0,
'ngangla' => 3,
'langthel' => 2
);
$sum = ArrayHelper::copyKeys($arr1);
ArrayHelper::addArrays($sum, $arr1);
ArrayHelper::addArrays($sum, $arr2);
It can also be done using array_walk:
function array_sum_values(array $input, $key) {
$sum = 0;
array_walk($input, function($item, $index, $params) {
if (!empty($item[$params[1]]))
$params[0] += $item[$params[1]];
}, array(&$sum, $key)
);
return $sum;
}
var_dump(array_sum_values($arr, 'gozhi'));
Not so readable like previous solutions but it works :)
Go through each item of the array and sum values to previous values if they exist, if not just assign the value.
<?php
$array =
[
[
'a'=>1,
'b'=>1,
'c'=>1,
],
[
'a'=>2,
'b'=>2,
],
[
'a'=>3,
'd'=>3,
]
];
$result = array_reduce($array, function($carry, $item) {
foreach($item as $k => $v)
$carry[$k] = $v + ($carry[$k] ?? 0);
return $carry;
}, []);
print_r($result);
Output:
Array
(
[a] => 6
[b] => 3
[c] => 1
[d] => 3
)
Or just loop through each sub array, and group the values for each column. Eventually summing them:
foreach($array as $subarray)
foreach($subarray as $key => $value)
$grouped[$key][] = $value;
$sums = array_map('array_sum', $grouped);
Here's a version where the array keys may not be the same for both arrays, but you want them all to be there in the final array.
function array_add_by_key( $array1, $array2 ) {
foreach ( $array2 as $k => $a ) {
if ( array_key_exists( $k, $array1 ) ) {
$array1[$k] += $a;
} else {
$array1[$k] = $a;
}
}
return $array1;
}
We need to check first if array key does exist.
CODE:
$sum = array();
foreach ($array as $key => $sub_array) {
foreach ($sub_array as $sub_key => $value) {
//If array key doesn't exists then create and initize first before we add a value.
//Without this we will have an Undefined index error.
if( ! array_key_exists($sub_key, $sum)) $sum[$sub_key] = 0;
//Add Value
$sum[$sub_key]+=$value;
}
}
print_r($sum);
OUTPUT With Array Key Validation:
Array
(
[gozhi] => 10
[uzorong] => 1
[ngangla] => 8
[langthel] => 10
)
OUTPUT Without Array Key Validation:
Notice: Undefined index: gozhi in F:\web\index.php on line 37
Notice: Undefined index: uzorong in F:\web\index.php on line 37
Notice: Undefined index: ngangla in F:\web\index.php on line 37
Notice: Undefined index: langthel in F:\web\index.php on line 37
Array
(
[gozhi] => 10
[uzorong] => 1
[ngangla] => 8
[langthel] => 10
)
This is a bad practice although it prints the output. Always check first if key does exist.
Simple example with array_reduce()
$numbers = [10,20,30];
$total = 0;
foreach ($numbers as $number) {
$total += $number;
}
echo $total; // 60
=>
$numbers = [10,20,30];
$total = array_reduce($numbers, function ($previous, $current) {
return $previous + $current;
});
echo $total; // 60
With assoc array:
$carts = [
['item' => 'A', 'qty' => 2, 'price' => 10],
['item' => 'B', 'qty' => 3, 'price' => 20],
['item' => 'C', 'qty' => 5, 'price' => 30]
];
$total = array_reduce(
$carts,
function ($prev, $item) {
return $prev + $item['qty'] * $item['price'];
}
);
echo $total; // 155
More info => https://www.phptutorial.net/php-tutorial/php-array_reduce/
For those who landed here and are searching for a solution that merges N arrays AND also sums the values of identical keys found in the N arrays, I've written this function that works recursively as well. (See: https://gist.github.com/Nickology/f700e319cbafab5eaedc)
Example:
$a = array( "A" => "bob", "sum" => 10, "C" => array("x","y","z" => 50) );
$b = array( "A" => "max", "sum" => 12, "C" => array("x","y","z" => 45) );
$c = array( "A" => "tom", "sum" => 8, "C" => array("x","y","z" => 50, "w" => 1) );
print_r(array_merge_recursive_numeric($a,$b,$c));
Will result in:
Array
(
[A] => tom
[sum] => 30
[C] => Array
(
[0] => x
[1] => y
[z] => 145
[w] => 1
)
)
Here's the code:
<?php
/**
* array_merge_recursive_numeric function. Merges N arrays into one array AND sums the values of identical keys.
* WARNING: If keys have values of different types, the latter values replace the previous ones.
*
* Source: https://gist.github.com/Nickology/f700e319cbafab5eaedc
* #params N arrays (all parameters must be arrays)
* #author Nick Jouannem <nick#nickology.com>
* #access public
* #return void
*/
function array_merge_recursive_numeric() {
// Gather all arrays
$arrays = func_get_args();
// If there's only one array, it's already merged
if (count($arrays)==1) {
return $arrays[0];
}
// Remove any items in $arrays that are NOT arrays
foreach($arrays as $key => $array) {
if (!is_array($array)) {
unset($arrays[$key]);
}
}
// We start by setting the first array as our final array.
// We will merge all other arrays with this one.
$final = array_shift($arrays);
foreach($arrays as $b) {
foreach($final as $key => $value) {
// If $key does not exist in $b, then it is unique and can be safely merged
if (!isset($b[$key])) {
$final[$key] = $value;
} else {
// If $key is present in $b, then we need to merge and sum numeric values in both
if ( is_numeric($value) && is_numeric($b[$key]) ) {
// If both values for these keys are numeric, we sum them
$final[$key] = $value + $b[$key];
} else if (is_array($value) && is_array($b[$key])) {
// If both values are arrays, we recursively call ourself
$final[$key] = array_merge_recursive_numeric($value, $b[$key]);
} else {
// If both keys exist but differ in type, then we cannot merge them.
// In this scenario, we will $b's value for $key is used
$final[$key] = $b[$key];
}
}
}
// Finally, we need to merge any keys that exist only in $b
foreach($b as $key => $value) {
if (!isset($final[$key])) {
$final[$key] = $value;
}
}
}
return $final;
}
?>
Here you have how I usually do this kind of operations.
// We declare an empty array in wich we will store the results
$sumArray = array();
// We loop through all the key-value pairs in $myArray
foreach ($myArray as $k=>$subArray) {
// Each value is an array, we loop through it
foreach ($subArray as $id=>$value) {
// If $sumArray has not $id as key we initialize it to zero
if(!isset($sumArray[$id])){
$sumArray[$id] = 0;
}
// If the array already has a key named $id, we increment its value
$sumArray[$id]+=$value;
}
}
print_r($sumArray);
You can try this:
$c = array_map(function () {
return array_sum(func_get_args());
},$a, $b);
and finally:
print_r($c);
this works great on my laravel project
print_r($Array); // your original array
$_SUM = [];
// count($Array[0]) => if the number of keys are equall in all arrays then do a count of index 0 etc.
for ($i=0; $i < count($Array[0]); $i++) {
$_SUM[] = $Array[0][$i] + $Array[1][$i]; // do a for loop on the count
}
print_r($_SUM); // get a sumed up array
$sumArray = array();
foreach ($myArray as $k => $subArray) {
foreach ($subArray as $id => $value) {
if (!isset($sumArray[$id])) {
$sumArray[$id] = 0;
}
$sumArray[$id]+=$value;
}
}
$sumArray = array();
foreach ($myArray as $k=>$subArray) {
foreach ($subArray as $id=>$value) {
if(!isset($sumArray[$id])){
$sumArray[$id] =$value;
}else {
$sumArray[$id]+=$value;
}
}
}
print_r($sumArray);
`
For example, you can pluck all fields from a result like this below.
I am picking out the 'balance' from an array and save to a variable
$kii = $user->pluck('balance');
then on the next line u can sum like this:
$sum = $kii->sum();
Hope it helps.
Code here:
$temp_arr = [];
foreach ($a as $k => $v) {
if(!is_null($v)) {
$sum = isset($temp_arr[$v[0]]) ? ((int)$v[5] + $sum) : (int)$v[5];
$temp_arr[$v[0]] = $sum;
}
}
return $temp_arr;
Result:
{SEQ_OK: 1328,SEQ_ERROR: 561}
How would you flip 90 degrees (transpose) a multidimensional array in PHP? For example:
// Start with this array
$foo = array(
'a' => array(
1 => 'a1',
2 => 'a2',
3 => 'a3'
),
'b' => array(
1 => 'b1',
2 => 'b2',
3 => 'b3'
),
'c' => array(
1 => 'c1',
2 => 'c2',
3 => 'c3'
)
);
$bar = flipDiagonally($foo); // Mystery function
var_dump($bar[2]);
// Desired output:
array(3) {
["a"]=>
string(2) "a2"
["b"]=>
string(2) "b2"
["c"]=>
string(2) "c2"
}
How would you implement flipDiagonally()?
Edit: this is not homework. I just want to see if any SOers have a more creative solution than the most obvious route. But since a few people have complained about this problem being too easy, what about a more general solution that works with an nth dimension array?
i.e. How would you write a function so that:
$foo[j][k][...][x][y][z] = $bar[z][k][...][x][y][j]
?(ps. I don't think 12 nested for loops is the best solution in this case.)
function transpose($array) {
array_unshift($array, null);
return call_user_func_array('array_map', $array);
}
Or if you're using PHP 5.6 or later:
function transpose($array) {
return array_map(null, ...$array);
}
With 2 loops.
function flipDiagonally($arr) {
$out = array();
foreach ($arr as $key => $subarr) {
foreach ($subarr as $subkey => $subvalue) {
$out[$subkey][$key] = $subvalue;
}
}
return $out;
}
I think you're referring to the array transpose (columns become rows, rows become columns).
Here is a function that does it for you (source):
function array_transpose($array, $selectKey = false) {
if (!is_array($array)) return false;
$return = array();
foreach($array as $key => $value) {
if (!is_array($value)) return $array;
if ($selectKey) {
if (isset($value[$selectKey])) $return[] = $value[$selectKey];
} else {
foreach ($value as $key2 => $value2) {
$return[$key2][$key] = $value2;
}
}
}
return $return;
}
Transposing an N-dimensional array:
function transpose($array, &$out, $indices = array())
{
if (is_array($array))
{
foreach ($array as $key => $val)
{
//push onto the stack of indices
$temp = $indices;
$temp[] = $key;
transpose($val, $out, $temp);
}
}
else
{
//go through the stack in reverse - make the new array
$ref = &$out;
foreach (array_reverse($indices) as $idx)
$ref = &$ref[$idx];
$ref = $array;
}
}
$foo[1][2][3][3][3] = 'a';
$foo[4][5][6][5][5] = 'b';
$out = array();
transpose($foo, $out);
echo $out[3][3][3][2][1] . ' ' . $out[5][5][6][5][4];
Really hackish, and probably not the best solution, but hey it works.
Basically it traverses the array recursively, accumulating the current indicies in an array.
Once it gets to the referenced value, it takes the "stack" of indices and reverses it, putting it into the $out array. (Is there a way of avoiding use of the $temp array?)
Codler's answer fails for a single-row matrix (e.g. [[1,2]]) and also for the empty matrix ([]), which must be special-cased:
function transpose(array $matrix): array {
if (!$matrix) return [];
return array_map(count($matrix) == 1 ? fn ($x) => [$x] : null, ...$matrix);
}
(note: PHP 7.4+ syntax, easy enough to adapt for older versions)
I got confronted with the same problem. Here is what i came up with:
function array_transpose(array $arr)
{
$keys = array_keys($arr);
$sum = array_values(array_map('count', $arr));
$transposed = array();
for ($i = 0; $i < max($sum); $i ++)
{
$item = array();
foreach ($keys as $key)
{
$item[$key] = array_key_exists($i, $arr[$key]) ? $arr[$key][$i] : NULL;
}
$transposed[] = $item;
}
return $transposed;
}
I needed a transpose function with support for associative array:
$matrix = [
['one' => 1, 'two' => 2],
['one' => 11, 'two' => 22],
['one' => 111, 'two' => 222],
];
$result = \array_transpose($matrix);
$trans = [
'one' => [1, 11, 111],
'two' => [2, 22, 222],
];
And the way back:
$matrix = [
'one' => [1, 11, 111],
'two' => [2, 22, 222],
];
$result = \array_transpose($matrix);
$trans = [
['one' => 1, 'two' => 2],
['one' => 11, 'two' => 22],
['one' => 111, 'two' => 222],
];
The array_unshift trick did not work NOR the array_map...
So I've coded a array_map_join_array function to deal with record keys association:
/**
* Similar to array_map() but tries to join values on intern keys.
* #param callable $callback takes 2 args, the intern key and the list of associated values keyed by array (extern) keys.
* #param array $arrays the list of arrays to map keyed by extern keys NB like call_user_func_array()
* #return array
*/
function array_map_join_array(callable $callback, array $arrays)
{
$keys = [];
// try to list all intern keys
array_walk($arrays, function ($array) use (&$keys) {
$keys = array_merge($keys, array_keys($array));
});
$keys = array_unique($keys);
$res = [];
// for each intern key
foreach ($keys as $key) {
$items = [];
// walk through each array
array_walk($arrays, function ($array, $arrKey) use ($key, &$items) {
if (isset($array[$key])) {
// stack/transpose existing value for intern key with the array (extern) key
$items[$arrKey] = $array[$key];
} else {
// or stack a null value with the array (extern) key
$items[$arrKey] = null;
}
});
// call the callback with intern key and all the associated values keyed with array (extern) keys
$res[$key] = call_user_func($callback, $key, $items);
}
return $res;
}
and array_transpose became obvious:
function array_transpose(array $matrix)
{
return \array_map_join_array(function ($key, $items) {
return $items;
}, $matrix);
}
We can do this by using Two foreach. Traveling one array and another array to create new arrayLike This:
$foo = array(
'a' => array(
1 => 'a1',
2 => 'a2',
3 => 'a3'
),
'b' => array(
1 => 'b1',
2 => 'b2',
3 => 'b3'
),
'c' => array(
1 => 'c1',
2 => 'c2',
3 => 'c3'
)
);
$newFoo = [];
foreach($foo as $a => $k){
foreach($k as $i => $j){
$newFoo[$i][]= $j;
}
}
Check The Output
echo "<pre>";
print_r($newFoo);
echo "</pre>";
Before I start, I'd like to say thanks again to #quazardus for posting his generalised solution for tranposing any two dimenional associative (or non-associative) array!
As I am in the habit of writing my code as tersely as possible I went on to "minimizing" his code a little further. This will very likely not be to everybody's taste. But just in case anyone should be interested, here is my take on his solution:
function arrayMap($cb, array $arrays) // $cb: optional callback function
{ $keys = [];
array_walk($arrays, function ($array) use (&$keys)
{ $keys = array_merge($keys, array_keys($array)); });
$keys = array_unique($keys); $res = [];
foreach ($keys as $key) {
$items = array_map(function ($arr) use ($key)
{return isset($arr[$key]) ? $arr[$key] : null; },$arrays);
$res[$key] = call_user_func(
is_callable($cb) ? $cb
: function($k, $itms){return $itms;},
$key, $items);
}
return $res;
}
Now, analogous to the PHP standard function array_map(), when you call
arrayMap(null,$b);
you will get the desired transposed matrix.
This is another way to do the exact same thing which #codler s answer does. I had to dump some arrays in csv so I used the following function:
function transposeCsvData($data)
{
$ct=0;
foreach($data as $key => $val)
{
//echo count($val);
if($ct< count($val))
$ct=count($val);
}
//echo $ct;
$blank=array_fill(0,$ct,array_fill(0,count($data),null));
//print_r($blank);
$retData = array();
foreach ($data as $row => $columns)
{
foreach ($columns as $row2 => $column2)
{
$retData[$row2][$row] = $column2;
}
}
$final=array();
foreach($retData as $k=>$aval)
{
$final[]=array_replace($blank[$k], $aval);
}
return $final;
}
Test and output reference: https://tutes.in/how-to-transpose-an-array-in-php-with-irregular-subarray-size/
Here is array_walk way to achieve this,
function flipDiagonally($foo){
$temp = [];
array_walk($foo, function($item,$key) use(&$temp){
foreach($item as $k => $v){
$temp[$k][$key] = $v;
}
});
return $temp;
}
$bar = flipDiagonally($foo); // Mystery function
Demo.
Here's a variation of Codler/Andreas's solution that works with associative arrays. Somewhat longer but loop-less purely functional:
<?php
function transpose($array) {
$keys = array_keys($array);
return array_map(function($array) use ($keys) {
return array_combine($keys, $array);
}, array_map(null, ...array_values($array)));
}
Example:
<?php
$foo = array(
"fooA" => [ "a1", "a2", "a3"],
"fooB" => [ "b1", "b2", "b3"],
"fooC" => [ "c1", "c2", "c3"]
);
print_r( transpose( $foo ));
// Output like this:
Array (
[0] => Array (
[fooA] => a1
[fooB] => b1
[fooC] => c1
)
[1] => Array (
[fooA] => a2
[fooB] => b2
[fooC] => c2
)
[2] => Array (
[fooA] => a3
[fooB] => b3
[fooC] => c3
)
);