PHP sum by other field from unique id in array - php

I have an multidimensional array like this one:
Array
(
[0] => array('id'=>1,'name'=>'Agent 1','total'=>3)
[1] => array('id'=>2,'name'=>'Agent 2','total'=>3)
[2] => array('id'=>3,'name'=>'Agent 3','total'=>3)
[3] => array('id'=>1,'name'=>'Agent 1','total'=>6)
)
And I want to remove duplicate agents from this array and sum the total field to end up in a array like this:
Array
(
[0] => array('id'=>1,'name'=>'Agent 1','total'=>9)
[1] => array('id'=>2,'name'=>'Agent 2','total'=>3)
[2] => array('id'=>3,'name'=>'Agent 3','total'=>3)
)
I have tried array_unique but it only remove duplicates...

Try this code: sandbox code
Main idea of algorithm - caching key pairs in result array and further checking existence of them.
$array = [
0 => ['id' => 1, 'name' => 'Agent 1', 'total' => 3],
1 => ['id' => 2, 'name' => 'Agent 2', 'total' => 3],
2 => ['id' => 3, 'name' => 'Agent 3', 'total' => 3],
3 => ['id' => 1, 'name' => 'Agent 1', 'total' => 6],
];
$sumArray = [];
foreach ($array as $agentInfo) {
// create new item in result array if pair 'id'+'name' not exists
if (!isset($sumArray[$agentInfo['id'].$agentInfo['name']])) {
$sumArray[$agentInfo['id'].$agentInfo['name']] = $agentInfo;
} else {
// apply sum to existing element otherwise
$sumArray[$agentInfo['id'].$agentInfo['name']]['total'] += $agentInfo['total'];
}
}
// optional action to flush keys of array
$sumArray = array_values($sumArray);
print_r ($sumArray);

Try this,
$arrays = array_values(array_combine(array_map(function ($i) { return $i['id']; }, $array), $array));
print_r($arrays);
DEMO

assuming an id is unique you could use this
$array = array(
array('id' => 1, 'name' => 'Agent 1', 'total' => 3),
array('id' => 2, 'name' => 'Agent 2', 'total' => 3),
array('id' => 3, 'name' => 'Agent 3', 'total' => 3),
array('id' => 1, 'name' => 'Agent 1', 'total' => 6)
);
$array_ids = array();
foreach ($array as $key => $value) {
if (isset($array_ids[$value['id']])) {
$array[$array_ids[$value['id']]]['total'] += $value['total'];
unset($array[$key]);
}
else
{
$array_ids[$value['id']] = $key;
}
}
in this way you save the used id's into array $array_ids, with that you can easily check if an agent already exists in the array

In order to achieve the exact output you desire, you will need a nested loop.
$input = array(
0 => array('id'=>1,'name'=>'Agent 1','total'=>3),
1 => array('id'=>2,'name'=>'Agent 2','total'=>3),
2 => array('id'=>3,'name'=>'Agent 3','total'=>3),
3 => array('id'=>1,'name'=>'Agent 1','total'=>6)
);
// This is where we will save our result
$output = array();
foreach ($input as $item) {
// Used to determine if the current $item
// already exists in the $output array
$foundItem = false;
// Now we check the $item against every output entry
foreach ($output as &$entry) {
if ($entry["id"] == $item["id"]) {
// Since we found a match, let's add the
//current item's total to the output total.
$entry["total"] += $item["total"];
// Marking this as true will later prevent us
// from inserting the item to the output array twice
$foundItem = true;
}
}
// If the item was not found in the output array
// the $foundItem variable remains false
// Using ! to negate the boolean, we insert the item to the output array
if (!$foundItem) {
$output[] = $item;
}
}
Realize that this is not the only way to get the required output. This only is the simplest solution and can certainly be improved in many ways. However, I will leave that part up to you.

I have tried using array_reduce - Iteratively reduce the array to a single value using a callback function. And written a function according to the requirements. Hope this will help you.
<?php
$array = [
0 => ['id' => 1, 'name' => 'Agent 1', 'total' => 3],
1 => ['id' => 2, 'name' => 'Agent 2', 'total' => 3],
2 => ['id' => 3, 'name' => 'Agent 3', 'total' => 3],
3 => ['id' => 1, 'name' => 'Agent 1', 'total' => 6],
];
$result = array_reduce($array, function($temp, $item){
isset($temp[$item['id']])
? $temp[$item['id']]['total'] += $item['total']
: $temp[$item['id']] = $item;
return $temp;
}, []);
print_r($result);
?>
OUTPUT
Array
(
[1] => Array ( [id] => 1 [name] => Agent 1 [total] => 9 )
[2] => Array ( [id] => 2 [name] => Agent 2 [total] => 3 )
[3] => Array ( [id] => 3 [name] => Agent 3 [total] => 3 )
)

Related

Loop into multidimensional array from top to bottom

I have this tree :
Array
(
[0] => Array
(
[id] => 1
[parent_id] => 0
[title] => Parent Page
[children] => Array
(
[0] => Array
(
[id] => 2
[parent_id] => 1
[title] => Sub Page
),
[1] => Array
(
[id] => 5
[parent_id] => 1
[title] => Sub Page 2
)
)
)
[1] => Array
(
[id] => 4
[parent_id] => 0
[title] => Another Parent Page
)
)
And I'm looking for a display from top to bottom.
And display something like this :
1
1.2
1.5
4
But if I have id 3 which is a leaf from 5 I would like this :
1
1.2
1.5
1.5.3
4
I have search a lot and my brain is limited when i'm using recursivity..
I have tried this :
function printAll($a){
foreach ($a as $v){
if (!array_key_exists('children', $v)){
debugLog($v['id']);
return;
}
else{
$arrayChildrens = $v['children'];
foreach($arrayChildrens as $c){
$arrayChildrens = $c['children'];
$this->printAll($arrayChildrens);
}
}
}
}
But doesn't work..
I tried to begin just to display
1
2
5
4
But my goal is to display id parents before id ( like Ishowed you before)
Thanks a lot !
This function should give you your expected output.
function printAll($a, $prefix = '') {
//loop through $a
foreach($a as $v) {
//echo current level `id` with previous `$prefix`
echo "{$prefix}{$v['id']}\n";
//check if current level contains children
if(!empty($v['children'])) {
//clean up prefix to remove extra `.` at the end of prefixes
$prev_prefix = rtrim($prefix, '.');
//recurse printAll again passing the children as `$a` and a `$prefix` being the previous levels combined e.g `1.5`
//also clean up extra periods at the start of the prefix
printAll($v['children'], ltrim("{$prev_prefix}.{$v['id']}.", "."));
}
}
}
Output:
1
1.2
1.5
1.5.3
4
Using a proper return
Usually with a function you actually want the function to return values instead of echoing them automatically to your page. If you want this function to return an array of values instead of echoing them, you could do this:
function printAll($a, $level = '', $values = []) {
foreach($a as $v) {
$values[] = $value = "{$level}{$v['id']}";
if(!empty($v['children'])) {
$values = printAll($v['children'], "{$value}.", $values);
}
}
return $values;
}
Which will have a result like this:
Array
(
[0] => 1
[1] => 1.2
[2] => 1.5
[3] => 1.5.3
[4] => 4
)
This should do the job.
$arr = array(
array(
'id' => 1,
'parent_id' => 0,
'title' => 'Parent Page',
'children' => array(
array(
'id' => 2,
'parent_id' => 1,
'title' => 'Sub Page',
),
array(
'id' => 5,
'parent_id' => 1,
'title' => 'Sub Page 2',
'children' => array(
array(
'id' => 7,
'parent_id' => 5,
'title' => 'Sub Page',
),
array(
'id' => 8,
'parent_id' => 5,
'title' => 'Sub Page 2',
)
)
)
)
),
array(
'id' => 4,
'parent_id' => 0,
'title' => 'Another Parent Page',
)
);
function printAll($arr, $parent = [])
{
if (is_array($arr)) {
foreach ($arr as $k => $v) {
if (isset($v['id'])) {
$parent[] = $v['id'];
echo implode('.', $parent) . PHP_EOL;
}
if (isset($v['children'])) {
printAll($v['children'], $parent);
}
array_pop($parent);
}
}
}
printAll($arr);
Output
1
1.2
1.5
1.5.7
1.5.8
4
Working demo.

How to subtract all column values in multi-dimensional array in Php?

How can I subtract all the columns values? My array is like
Array
(
[0] => Array
(
[id] => 1
[web_traffic] => 442
[form_users] => 131
[date] => 20181004
)
[1] => Array
(
[id] => 2
[web_traffic] => 102
[form_users] => 15
[date] => 20181003
)
[2] => Array
(
[id] => 3
[web_traffic] => 387
[form_users] => 97
[date] => 20181002
)
)
I need to subtract the each column(except date & id) and get the result based on date(Ascending order). For example 20181004 means 4th October 2018. My output should like the below
Array
(
[web_traffic] => -152
[form_users] => -49
)
My code took reference from How to sum all column values in multi-dimensional array?
foreach ($data as $value) {
unset($value[ 'id' ]);
$time = date('Ymd', strtotime($value[ 'date' ]));
if (in_array($time, $dates)) {
$value[ 'date' ] = $time;
foreach ($value as $key => $secondValue) {
if ( !isset($output[ $key ])) {
$output[ $key ] = 0;
}
$output[ $key ] -= $secondValue;
}
}
}
Use PHP array_reduce() and array_column() like this:
$initial_array = array(array('id' => 1,
'web_traffic' => 442,
'form_users' => 131,
'date' => 20181004),
array('id' => 2,
'web_traffic' => 102,
'form_users' => 15,
'date' => 20181003),
array('id' => 3,
'web_traffic' => 387,
'form_users' => 97,
'date' => 20181002));
function sum($carry, $item)
{
$carry -= $item;
return $carry;
}
$web_traffic = array_column($initial_array, "web_traffic");
$form_users = array_column($initial_array, "form_users");
$date = array_column($initial_array, "date");
array_multisort($date, SORT_ASC, $form_users, SORT_DESC, $initial_array);
$result_array = Array(
"web_traffic" => array_reduce(array_column($initial_array, "web_traffic"), "sum",2*$initial_array[0]['web_traffic']),
"form_users" => array_reduce(array_column($initial_array, "form_users"), "sum",2*$initial_array[0]['form_users'])
);
print_r($result_array);
I would first sort the array using usort() in example.
Sorting first, because that can be tricky to substract in 1 loop the oldest datas to the newers one.
Separating intentions provide a cleaner code, easier to maintain.
The dates don't need to be converted into a date. The string is well formatted and can be used as is in a comparison and a sort. "20170101" < "20180101", "20180101" < "20181001" and "20181002" < "20181004"
When done, I'll save the first values as initial values to be used in substract.
Then, loop the sorted array and substract the 'web_traffic' and 'form_users' to the initial values.
$datas = array(array('id' => 1,
'web_traffic' => 442,
'form_users' => 131,
'date' => 20181004),
array('id' => 2,
'web_traffic' => 102,
'form_users' => 15,
'date' => 20181003),
array('id' => 3,
'web_traffic' => 387,
'form_users' => 97,
'date' => 20181002));
//If needed, you can backup the original array, because usort() will modify it.
$backUpDatas = $datas;
//Sort
usort($datas, function ($arr1, $arr2)
{
if (isset($arr1['date']) && isset($arr2['date']))
{
if ($arr1['date'] == $arr2['date'])
return (0);
return (($arr1['id'] > $arr2['id']) ? (-1) : (1));
}
});
//Initial values
$finalValues['web_traffic'] = $datas[0]['web_traffic'];
$finalValues['form_users'] = $datas[0]['form_users'];
//Substract
foreach ($datas as $key => $value)
{
if ($key > 0)
{
if (isset($value['web_traffic']))
$finalValues['web_traffic'] -= $value['web_traffic'];
if (isset($value['form_users']))
$finalValues['form_users'] -= $value['form_users'];
}
}
var_dump($finalValues);
Output :
array (size=2)
'web_traffic' => int -157
'form_users' => int -49

Appending multiple values to associative array in PHP [duplicate]

This question already has answers here:
Merge two 2d arrays by shared column value
(6 answers)
Closed last month.
I would like to create a PHP array that will end up looking like this:
idPlayer namePlayer Points
1 John 20
2 Sam 25
3 Ben 22
But I would like to append the values not all at once:
First append the idPlayer and namePlayer
Then append the points for that idPlayer (I would have a $idPlayer variable to use each time I loop)
How could I do this in PHP?
I was thinking:
$myArray['idPlayer'] = "1";
$myArray['namePlayer'] = "John";
$myArray['Points'] = "20"
And then, how would I tell the array to go to the next row?
// output array
$myArray = array();
// your loop
while (something) {
// make an array for this iteration
$itarr = array();
// put stuff in it
$itarr['idPlayer'] = "1";
$itarr['namePlayer'] = "John";
$itarr['Points'] = "20"
// append it to output array using standard array indexing
$myArray[] = $itarr;
// OR, your own index
$myArray[$itarr['idPlayer']] = $itarr;
}
I dont know why you want to achieve such thing. But consider this example:
// like player table array initial
$players = array(
array(
'idPlayer' => 1,
'namePlayer' => 'John',
),
array(
'idPlayer' => 2,
'namePlayer' => 'Sam',
),
array(
'idPlayer' => 3,
'namePlayer' => 'Ben',
),
);
// data points to be added later (like points table)
$points = array(
array(
'idPlayer' => 1,
'Points' => 20,
),
array(
'idPlayer' => 2,
'Points' => 25,
),
array(
'idPlayer' => 3,
'Points' => 22,
),
);
$myArray = array();
foreach($players as $key => $value) {
foreach($points as $index => $element) {
// if this particular id matches the record inside points table then merge
if($value['idPlayer'] == $element['idPlayer']) {
$myArray[] = array('idPlayer' => $value['idPlayer'], 'namePlayer' => $value['namePlayer'], 'Points' => $element['Points']);
}
}
}
Should output something like: (can be used in a tabular data)
Array
(
[0] => Array
(
[idPlayer] => 1
[namePlayer] => John
[Points] => 20
)
[1] => Array
(
[idPlayer] => 2
[namePlayer] => Sam
[Points] => 25
)
[2] => Array
(
[idPlayer] => 3
[namePlayer] => Ben
[Points] => 22
)
)
Use the idPlayer as index:
$players = [
1 => [
'namePlayer' => 'John',
'points' => 20,
],
5 => [
'namePlayer' => 'Seth',
'points' => 25,
],
13 => [
'namePlayer' => 'Ben',
'points' => 35,
],
];
var_dump($players);
http://codepad.viper-7.com/nSXmZF
In php <5.4 use array() instead of [] constructor.

need soting multi dimensional array based on another array displaying order

I need sorting array based another array sort value.
Actual array : array(name=>'JK',age=>'20',place=>'India',year=>array(marks1=>array(sub1=>50,sub3=>70,sub7=>65,sub5=>75,sub4=>35), marks2=>array(sub8=>50,sub10=>70,sub12=>75,sub9=>35,sub11=>65))
sorting order array : array(name=>1,year=>2,age=>3,place=>4,sub1=>5,sub3=>6,sub4=>7,sub5=>8,sub7=>9,sub8=>10,sub9=>11,sub10=>12,sub11=>13,sub12=>14)
expected result array:
array(
name=>'JK',
year=>array(
marks1=>array(
sub1=>50,
sub3=>70,
sub4=>35,
sub5=>75
sub7=>65
),
marks2=>array(
sub8=>50,
sub9=>35,
sub10=>70,
sub11=>65,
sub12=>75
),
age=>'20',
place=>'India'
)
I hope this will help :)
$array1 = array(name=>'JK',age=>'20',place=>'India',year=>array(marks1=>array(sub1=>50,sub3=>70,sub7=>65,sub5=>75,sub4=>35), marks2=>array(sub8=>50,sub10=>70,sub12=>75,sub9=>35,sub11=>65));
$array2 = array(name=>1,year=>2,age=>3,place=>4,sub1=>5,sub3=>6,sub4=>7,sub5=>8,sub7=>9,sub8=>10,sub9=>11,sub10=>12,sub11=>13,sub12=>14);
//final array
$final_array = array();
//for each value in sorting array
foreach ($array2 as $key => $value)
{
//store result in final array
$final_array[$value] = $array1[$key];
}
//display array for check result
var_dump($final_array);
I am not exactly sure what is being asked. However, I will take a shot. I think the function you are looking for is uksort.
<?php
$array1 = array(name=>'JK',age=>'20',place=>'India',year=>array(marks1=>array(sub1=>50,sub3=>70,sub7=>65,sub5=>75,sub4=>35), marks2=>array(sub8=>50,sub10=>70,sub12=>75,sub9=>35,sub11=>65)));
function sorter($a,$b)
{
$array2 = array(name=>1,year=>2,age=>3,place=>4,sub1=>5,sub3=>6,sub4=>7,sub5=>8,sub7=>9,sub8=>10,sub9=>11,sub10=>12,sub11=>13,sub12=>14);
return $array2[$a] > $array2[$b];
}
uksort($array1, "sorter");
var_dump($array1);
?>
Here is an example of it running on codepad. You will probably have to work out a bit more since the subs are not sorted. But, possibly is_array can help you out.
$arr1 = array(
'name' => 'JK',
'age' => 20,
'place' => 'India',
'year' =>
array(
'marks1' =>
array('sub1' => 50,
'sub3' => 70,
'sub7' => 65,
'sub5' => 75,
'sub4' => 35),
'marks2' =>
array('sub8' => 50,
'sub10' => 70,
'sub12' => 75,
'sub9' => 35,
'sub11' => 65)));
$arr2 = array('name' => 1, 'year' => 2, 'age' => 3, 'place' => 4, 'sub1' => 5, 'sub3' => 6, 'sub4' => 7, 'sub5' => 8, 'sub7' => 9, 'sub8' => 10, 'sub9' => 11, 'sub10' => 12, 'sub11' => 13, 'sub12' => 14);
foreach ($arr1['year'] as $key => &$value){
uksort($value, function ($a, $b) use($arr2){
return $arr2[$a] - $arr2[$b];
});
}
uksort($arr1, function ($a, $b) use($arr2){
return $arr2[$a] - $arr2[$b];
});
print_r($arr1);
Output:
Array
(
[name] => JK
[year] => Array
(
[marks1] => Array
(
[sub1] => 50
[sub3] => 70
[sub4] => 35
[sub5] => 75
[sub7] => 65
)
[marks2] => Array
(
[sub8] => 50
[sub9] => 35
[sub10] => 70
[sub11] => 65
[sub12] => 75
)
)
[age] => 20
[place] => India
)

Counting values within multidimensional arrays?

I have a large array where I basically need to count the number of uniques:
example array
The end result I am looking for something like
$result = array(
'A3D5' => array('P2' => 1, 'P3' => 1, 'P4' => 1, 'total' => 3),
'AE5S' => array('P4' => 1, 'total' => 1)
);
I've tried foreaching through but can't seem to figure out how to sort them into another key, something like $zone = "P{$array['zone']}"; $result[$zone][$prestige]++ or seems to kind of not work or just error.
$array = array(
"denizen" => array
(
"prestige" => 2,
"zone" => "A3D5",
"scope_frag" => 765
),
"생각" => array
(
"prestige" => 4,
"zone" => "A3D5",
"scope_frag" => 135
),
"Ryans" => array
(
"prestige" => 3,
"zone" => "A3D5",
"scope_frag" => 78
),
"지적인" => array
(
"prestige" => 2,
"zone" => "AE5S",
"scope_frag" => 481
)
);
foreach ($array as $group) {
$zones[$group["zone"]][] = $group["prestige"];
}
foreach ($zones as $name => $zone) {
$total = count($zone);
foreach ($zone as $prestige) {
$result[$name]["P".$prestige] += 1;
}
ksort($result[$name]);
$result[$name]["total"] = $total;
}
echo "<pre>";
print_r($result);
echo "</pre>";

Categories