PHP - sum values with the same key - php

What is the best way to sum the 'val' field with the same 'color' for each different color:
Array
(
[0] => Array
(
[color]=> "red"
[val]=> 4
)
[1] => Array
(
[color]=> "green"
[val]=> 3
)
[2] => Array
(
[color]=> "blue"
[val]=> 1
)
[3] => Array
(
[color]=> "green"
[val]=> 6
)
[4] => Array
(
[color]=> "blue"
[val]=> 2
)
)
Desired result : red: 4; green: 9; blue: 3.
Regards,
Elio Fernandes

I would do it this way, with a foreach loop:
$temp = [];
foreach($arr as $value) {
//check if color exists in the temp array
if(!array_key_exists($value['color'], $temp)) {
//if it does not exist, create it with a value of 0
$temp[$value['color']] = 0;
}
//Add up the values from each color
$temp[$value['color']] += $value['val'];
}

You can use array_reduce to have less and more readable code:
$array = array
(
0 => array("color"=> "red","val"=> 4),
1 => array("color"=> "blue","val"=> 3),
2 => array("color"=> "blue","val"=> 1)
);
function sum($accumulator, $item){
$accumulator[$item['color']] = $accumulator[$item['color']] ?? 0;
$accumulator[$item['color']] += $item['val'];
return $accumulator;
}
$sum = array_reduce($array, "sum");
var_dump($sum); // return here array(2) { ["red"]=> int(4) ["blue"]=> int(4) }
And the documentation: http://php.net/manual/en/function.array-reduce.php

I would loop it for readability's sake.
$output = array();
foreach($array as $value){
if(!isset($output[$value['color']])) $output[$value['color']] = 0; //Ensure the value is 0 if it doesn't exist
$output[$value['color']] += $value['val']; //Add the total for that color
}
This will return an array of colors to total counts.

Personally I'd do something like this with array_reduce, but as mentioned above it might not be the most readable option. Definitely a matter of opinion though.
$result = array_reduce($array, function ($carry, $item) {
$carry[$item['color']] = $carry[$item['color']] ?? 0;
$carry[$item['color']] += $item['val'];
return $carry;
}, []);
See https://eval.in/894644
Edit: Fix notices

To keep up the speed, without the foreach loop, I would say to do it like this:
$array = [
[
"color" => "red",
"val" => "4"
],
[
"color" => "green",
"val" => "3"
],
[
"color" => "blue",
"val" => "1"
],
[
"color" => "green",
"val" => "6"
],
[
"color" => "blue",
"val" => "2"
]
];
$array = array_values(array_reduce(
$array,
function (array $a, array $v) {
$k = "".$v['color']."";
if (!array_key_exists($k, $a)) {
$a[$k] = $v;
} else {
$a[$k]['val'] += $v['val'];
}
return $a;
},
array()
));
var_dump($array);

$arrays = array(
array(
"color"=>"red",
"val"=>4
),
array(
"color"=>"green",
"val"=>3
)
);
foreach ($color as $array) {
echo $array["color"].": ".$array["val"].";";
}

Related

get an array element where another element is known?

Array
(
[0] => Array
(
[what] => b4
[map] => 74,76,77,83
)
[1] => Array
(
[what] => b2
[map] => 53,82
)
[2] => Array
(
[what] => b1
[map] => 36
)
)
abc('b4');
function abc($what){
$map = // element `map` where `what` = $what;
}
So I need to get map where what is equal to $what;
For example - if $what is b4 result should be 74,76,77,83; and so on.
How can I do this?
If you are going to access the data on a regular basis and the what is unique, then use array_column() with the third parameter as the column to use as the key. Then your array is easily access with what and no loops are harmed in this answer...
$array = Array
(
Array
(
"what" => "b4",
"map" => "74,76,77,83"
),
Array
(
"what" => "b2",
"map" => "53,82"
),
Array
(
"what" => "b1",
"map" => "36"
)
);
$array = array_column($array, null, "what");
echo $array['b4']['map'];
gives...
74,76,77,83
With array_search() and array_column() you can get the matching $map in one line:
<?php
$array = Array
(
Array
(
"what" => "b4",
"map" => "74,76,77,83"
),
Array
(
"what" => "b2",
"map" => "53,82"
),
Array
(
"what" => "b1",
"map" => "36"
)
);
function abc($array, $what) {
return $array[array_search($what, array_column($array, 'what'))]['map'];
}
echo abc($array, "b4");
The function de-constructed and explained:
function abc($array /* the complete input array */, $what /* the search string */) {
// get the key of the sub-array that has $what in column 'what':
$key = array_search($what, array_column($array, 'what'));
// use that key to get 'map' on index $key
return $array[$key]['map'];
}
A working fiddle can be found here: https://3v4l.org/0NpcX
I think "walking" through an array is easy to read and understand:
<?php
$map = [
[
'what' => "b4",
'map' => "74,76,77,83"
],
[
'what' => "b2",
'map' => "53,82"
],
[
'what' => "b1",
'map' => "36"
]
];
function lookupWhatInMap(&$map, $what) {
array_walk($map, function($entry, $key) use ($what) {
if ($entry['what'] == $what) {
print_r($entry['map']);
}
});
}
lookupWhatInMap($map, "b4");
All you have to do is loop through your map and compare values.
function abc($what){
$map = [...];
foreach($map as $item) {
if (isset($item[$what]) ) {
return $item["map"];
}
}
return false;
}
If you just want 1 value from the array, you could use a foreach and a return statement where there is a match:
$a = [
[
"what" => "b4",
"map" => "74,76,77,83"
],
[
"what" => "b2",
"map" => "53,82"
],
[
"what" => "b1",
"map" => "36"
]
];
function abc($what, $arrays)
{
foreach ($arrays as $array) {
if ($array['what'] === $what) {
return $array['map'];
}
}
return false;
}
echo(abc('b4', $a)); // 74,76,77,83
Demo
https://ideone.com/V9WNNx
$arr[] = [
'what' => 'b4',
'map' => '74,76,77,83'
];
$arr[] = [
'what' => 'b2',
'map' => '53,82'
];
$arr[] = [
'what' => 'b1',
'map' => '36'
];
echo abc('b4', $arr);
function abc($what, $arr){
$map = null;
$idx = array_search($what, array_column($arr, 'what'));
if ($idx !== false) {
$map = $arr[$idx]['map'];
}
return $map;
}

Merge and diff arrays of arrays to find common values

I am trying to loop through several database table structures and determine the common structure (ie what columns are identical). The final structure should only show common columns, so if any table has a unique column, it should not be in the final array.
This is an example of what three table structures may look like.
$arr1 = [
["name"=>"col1", "type"=>"varchar"],
["name"=>"col2", "type"=>"int"]
];
$arr2 = [
["name"=>"col1", "type"=>"varchar"],
["name"=>"col2", "type"=>"int"] ,
["name"=>"col3", "type"=>"date"]
];
$arr3 = [
["name"=>"col1", "type"=>"varchar"],
["name"=>"col3", "type"=>"int"]
];
$arrays = [$arr1, $arr2, $arr3];
Using array_merge, array_diff, array_intersect, or a loop, is it possible to determine which of these columns are common to all tables?
The end value should be
[["name"=>"col1", "type"=>"varchar"]]
You could use array_uintersect, which does the intersection with your own function. But you should keep in mind, that the compare function does not simply returns true or false - instead you have to return -1, 0 or 1.
In PHP7 you could use the spaceship operator <=> for the comparison.
$intersection = array_uintersect($arr1, $arr2, $arr3, function($a, $b) {
$ret = $a['name'] <=> $b['name'];
return $ret ? $ret : $a['type'] <=> $b['type'];
});
print_r($intersection);
If you want to put all arrays inside the intersection, you could do this:
$arrays = [$arr1, $arr2, $arr3];
$arrays[] = function($a, $b) {
$ret = $a['name'] <=> $b['name'];
return $ret ? $ret : $a['type'] <=> $b['type'];
};
$intersection = array_uintersect(...$arrays);
In older versions, you should instead use strcasecmp.
You can use a custom compare method with array_uintersect():
$arr1 = [
["name" => "col1", "type" => "varchar"],
["name" => "col2", "type" => "int"]
];
$arr2 = [
["name" => "col1", "type" => "varchar"],
["name" => "col2", "type" => "int"],
["name" => "col3", "type" => "date"]
];
$arr3 = [
["name" => "col1", "type" => "varchar"],
["name" => "col3", "type" => "int"]
];
$common_columns = array_uintersect($arr1, $arr2, $arr3, 'compareDeepValue');
print_r($common_columns);
function compareDeepValue($val1, $val2)
{
return (strcasecmp(serialize($val1), serialize($val2))) ;
}
Will output:
Array
(
[0] => Array
(
[name] => col1
[type] => varchar
)
)
Note:
#Abracadaver made a good point this method will only work correctly when you have the array conventions in the same order.
Than you can for example use this:
function compareDeepValue($val1, $val2)
{
return ($val1['name'] === $val2['name'] && $val1['type'] === $val2['type']) ? 0 : -1;
}
You can extract the arrays and index by the name key and compute the intersection using the keys:
$result = array_intersect_key(array_column($arr1, null, 'name'),
array_column($arr2, null, 'name'),
array_column($arr3, null, 'name'));
Yields:
Array
(
[col1] => Array
(
[name] => col1
[type] => varchar
)
)
If needed, use array_values to get back to numeric indexes.
Maybe ?
$arrays = [$arr1, $arr2, $arr3];
$arrays_extended = [];
foreach($arrays as $row => $innerArray){
foreach($innerArray as $innerRow => $value){
array_push($arrays_extended, $value);
}
}
var_dump(array_unique($arrays_extended));
Outputs [["name"=>"col1", "type"=>"varchar"]]
Approach:
1.convert elements to strings as follows:
array(2) {
[0] =>
string(32) "{"name":"col1","type":"varchar"}"
[1] =>
string(28) "{"name":"col2","type":"int"}"
}
array(3) {
[0] =>
string(32) "{"name":"col1","type":"varchar"}"
[1] =>
string(28) "{"name":"col2","type":"int"}"
[2] =>
string(29) "{"name":"col3","type":"date"}"
}
array(2) {
[0] =>
string(32) "{"name":"col1","type":"varchar"}"
[1] =>
string(28) "{"name":"col3","type":"int"}"
}
2.Use array intersect to find common elements
3.convert back to arrays.
$arr1 = [
["name"=>"col1", "type"=>"varchar"],
["name"=>"col2", "type"=>"int"]
];
$arr2 = [
["name"=>"col1", "type"=>"varchar"],
["name"=>"col2", "type"=>"int"] ,
["name"=>"col3", "type"=>"date"]
];
$arr3 = [
["name"=>"col1", "type"=>"varchar"],
["name"=>"col3", "type"=>"int"]
];
list($darr1, $darr2, $darr3) = convertArrToStr($arr1, $arr2, $arr3);
/* output:
array(2) {
[0] =>
string(32) "{"name":"col1","type":"varchar"}"
[1] =>
string(28) "{"name":"col2","type":"int"}"
}
array(3) {
[0] =>
string(32) "{"name":"col1","type":"varchar"}"
[1] =>
string(28) "{"name":"col2","type":"int"}"
[2] =>
string(29) "{"name":"col3","type":"date"}"
}
array(2) {
[0] =>
string(32) "{"name":"col1","type":"varchar"}"
[1] =>
string(28) "{"name":"col3","type":"int"}"
}
*/
var_dump(duplicates($darr1, $darr2, $darr3));
/* output:
array(1) {
[0] =>
array(2) {
'name' =>
string(4) "col1"
'type' =>
string(7) "varchar"
}
}
*/
function convertArrToStr() {
$args = func_get_args();
foreach($args as &$arg){
foreach($arg as $k => $arr) {
$arg[$k] = json_encode($arr, true);
}
}
return $args;
}
function duplicates($darr1, $darr2, $darr3) {
$intersects = array_intersect($darr1, $darr2, $darr3);
$r = [];
foreach($intersects as $v) {
$r[] = json_decode($v, true);
}
return $r;
}
Hope this helps you write a more elegant solution.

PHP - Get key value from other key

I have the following array:
$array = Array(
"0" => Array (
"id" => 1081,
"name" => "John"
),
"1" => Array (
"id" => 1082,
"name" => "Matt"
),
"2" => Array (
"id" => 1083,
"name" => "Roger"
)
);
Is there anyway I can get name if I only know the id but without having to iterate through the array?
For PHP >= 5.5.0:
$id = 1082;
$result = array_column($array, 'name', 'id')[$id];
As Barmar points out, to get an array that is easy to use with id as the index:
$id = 1082;
$result = array_column($array, 'name', 'id');
echo $result[$id];
You can make an associative array that refers to the same elements, then use that:
function make_assoc(&$array, $keyname) {
$new_array = array();
foreach ($array as &$elt) {
$new_array[$elt[$keyname]] = $elt;
}
return $new_array;
}
$assoc_array = make_assoc($array, 'id');
Now you can use $assoc_array[1083] to access the third item in the original array. And since this returns an array of references, modifying that will also modify the element of the original array.
You can use array_map to search into your array if your PHP < 5.5.0 and you don't have array_column:
<?php
$array = Array(
"0" => Array (
"id" => 1081,
"name" => "John"
),
"1" => Array (
"id" => 1082,
"name" => "Matt"
),
"2" => Array (
"id" => 1083,
"name" => "Roger"
)
);
$find = 1082;
$value = '';
$arr = array_map(function($n) use ($find, &$value) {if ($n['id'] == $find) $value = $n['name']; }, $array);
print_r($value);
?>

Run throw array of associative array and find keys

I have two arrays that look like this:
(this one is ordered by value_max)
$max_values = [
["name" => "john", "id" => 5, "value_max" => 500],
["name" => "john", "id" => 3, "value_max" => 200],
...
];
$min_values = [
["name" => "john", "id" => 5, "value_min" => 100],
["name" => "john", "id" => 3, "value_min" => 150],
...
];
And I need to have a final array like this:
(This one stills need to be ordered by value_max, so I assume I could just overwrite the first array with the calculations done with the second)
$max_and_difference_values = [
["name" => "john", "id" => 5, "value_max" => 500, "difference_value" => 400],
["name" => "john", "id" => 3, "value_max" => 200, "difference_value" => 50 ],
...
];
My question is pretty straight: What is the best/effective way to run through both first arrays and build the last one. Assuming that the size of the arrays can be of around 150 elements.
To avoid looping through all arrays repeatedly, index one array by the field you want to merge on, i.e. the 'id' key:
$second = array_combine(array_map(function ($i) { return $i['id']; }, $second_array), $second_array);
Then looping through the other and comparing the values is pretty easy:
$third = array();
foreach ($first_array as $i) {
$third[] = $i + array('difference_value' => $i['value_max'] - $second[$i['id']]['value_min']);
}
If it's guaranteed that both arrays will have exactly matching keys, you don't even need the first step and just go by already existing keys.
This will sort your array. So you can sort the second array.
<?php
$min_values = array(
array("name" => "john", "id" => 5, "value_min" => 100),
array("name" => "john", "id" => 3, "value_min" => 150),
);
function aasort (&$array, $key) {
$sorter=array();
$ret=array();
reset($array);
foreach ($array as $ii => $va) {
$sorter[$ii]=$va[$key];
}
asort($sorter);
foreach ($sorter as $ii => $va) {
$ret[$ii]=$array[$ii];
}
$array=$ret;
}
aasort($min_values,"id");
echo "<pre>";
print_r($min_values);
echo "</pre>";
?>
And then you can use the logic what Alessandro Minoccheri mentioned.
$arr = array();
for ($i=0; $i<count($first_array);$i++){
$arr['name'] = $first_array[$i]['name'];
$arr['id'] = $first_array[$i]['id'];
$arr['value_max'] = $first_array[$i]['value_max'];
$arr['difference_value'] = $first_array[$i]['value_max']-$second[$i['id']]['value_max'] ;
}
As you have not shared how the second array with the minimum values is ordered (in itself and relative to the maximum values array), I'd say, index the minimum values by the id entry and then do the calculation in a second iteration. It should be fast enough, 150 elements is just "nothing":
$max_values = [
["name" => "john", "id" => 5, "value_max" => 500],
["name" => "john", "id" => 3, "value_max" => 200],
];
$min_values = [
["name" => "john", "id" => 5, "value_min" => 100],
["name" => "john", "id" => 3, "value_min" => 150],
];
$min_values_by_id = [];
foreach($min_values as $min) {
$min_values_by_id[$min['id']] = $min['value_min'];
}
$max_and_difference_values = $max_values;
foreach($max_and_difference_values as &$entry)
{
$entry['difference_value'] = $entry['value_max'] - $min_values_by_id[$entry['id']];
}
unset($entry);
print_r($max_and_difference_values);
This is just a straight forward example, nothing fancy. Demo
the following works for me. I'm not a PHP expert though, i.e. I'm not so familiar with cloning (note the empty clone array). Note that it only copies existing properties (if one array does contain a min_value and another does only contain a some other key)
In essence
function convertArr( $arr ) {
// Easy referencing without having to search through the arrays more than once for a matching id
$new_arr = array();
foreach( $arr as $array ) {
$new_arr[ $array['id'] ] = cloneArr( $array );
}
return $new_arr;
}
function merge( $arr1, $arr2 ) {
$convertedArr1 = convertArr( $arr1 );
$convertedArr2 = convertArr( $arr2 );
$arr = array();
// Based on the ordered array
foreach( $convertedArr1 as $array ) {
$id = $array['id'];
$tempArr = array();
foreach( $convertedArr1[ $id ] as $k => $v ) {
$tempArr[ $k ] = $v;
}
foreach( $convertedArr2[ $id ] as $k => $v ) {
$tempArr[ $k ] = $v;
}
array_push( $arr, $tempArr );
}
return $arr;
}
Full example:
<?php
//$arr1 = [ ["name" => "john", "id" => 5, "value_max" => 500], ["name" => "john", "id" => 3, "value_max" => 200] ];
//$arr2 = [ ["name" => "john", "id" => 5, "value_min" => 100], ["name" => "john", "id" => 3, "value_min" => 150] ];
$arr1 = array(
array( "name" => "john", "id" => 5, "value_max" => 500 ),
array( "name" => "john", "id" => 3, "value_max" => 200 )
);
$arr2 = array(
array( "name" => "john", "id" => 5, "value_min" => 100 ),
array( "name" => "john", "id" => 3, "value_min" => 150 )
);
function neatPrint( $arr ) {
echo "<pre>";
print_r( $arr );
echo "</pre>";
}
echo "<h2>Before</h2>";
neatPrint( $arr1 );
neatPrint( $arr2 );
echo "<hr/>";
function cloneArr( $old ) {
// I dunno how to properly clone
return $old;
}
function convertArr( $arr ) {
// Easy referencing without having to search through the arrays more than once for a matching id
$new_arr = array();
foreach( $arr as $array ) {
$new_arr[ $array['id'] ] = cloneArr( $array );
}
return $new_arr;
}
function merge( $arr1, $arr2 ) {
$convertedArr1 = convertArr( $arr1 );
$convertedArr2 = convertArr( $arr2 );
$arr = array();
// Based on the ordered array
foreach( $convertedArr1 as $array ) {
$id = $array['id'];
$tempArr = array();
neatPrint( $convertedArr1[ $id ] );
neatPrint( $convertedArr2[ $id ] );
foreach( $convertedArr1[ $id ] as $k => $v ) {
$tempArr[ $k ] = $v;
}
foreach( $convertedArr2[ $id ] as $k => $v ) {
$tempArr[ $k ] = $v;
}
array_push( $arr, $tempArr );
}
echo "<h2>Result</h2>";
return $arr;
}
echo "<h2>Loopthrough</h2>";
neatPrint( merge( $arr1, $arr2 ) );
?>
Let me know what you think :-)
if the element are in order try this code:
$arr = array();
for ($i=0; $i<count($first_array);$i++){
$arr['name'] = $first_array[$i]['name'];
$arr['id'] = $first_array[$i]['id'];
$arr['value_max'] = $first_array[$i]['value_max'];
$arr['difference_value'] = $first_array[$i]['value_max']-$second[$i['id']]['value_max'] ;
}

PHP - Deleting an entry from a multidimensional array

I have an array like this:
$_SESSION['food'] = array(
// ARRAY 1
array(
"name" => "apple",
"shape" => "round",
"color" => "red"
),
// ARRAY 2
array(
"name" => "banana",
"shape" => "long",
"color" => "yellow"
)
);
I want to search through all keys in all child arrays and delete the entire child array if the search term is found.
So, basically:
If searching for "long", the entire Array 2 is removed.
If searching for "apple", the entire Array 1 is removed.
How would I accomplish this?
Thanks!
This should do the trick:
foreach ($array as $key => $value) {
foreach ($value as $child_value) {
if ($child_value == $search_term) {
unset($array[$key]);
continue 2;
}
}
}
Depending on how many dimensions you have, you can use array_search.
I haven't tested the following, but it should work:
$unset = array_search('apple', $_SESSION['food']);
unset($_SESSION['food'][$unset]);
Here you go:
<?php
function deleteObjWithProperty($search,$arr)
{
foreach ($arr as &$val)
{
if (array_search($search,$val)!==false)
{
unlink($val);
}
}
return $arr;
}
?>
$_SESSION['food'] = array(
// ARRAY 1
array(
"name" => "apple",
"shape" => "round",
"color" => "red"
),
// ARRAY 2
array(
"name" => "banana",
"shape" => "long",
"color" => "yellow"
)
);
echo '<pre>'.print_r($_SESSION['food']).'</pre>';
$arr_food = array();
$search_term = 'apple';
foreach($_SESSION['food'] AS $arr) {
if($arr['name'] == $search_term) {
unset($arr);
}
$arr_food[] = $arr;
}
$_SESSION['food'] = $arr_food;
echo '<pre>'.print_r($_SESSION['food']).'</pre>';

Categories