Multi dimensional array, sum values of the same key - php

I have a multi dimensional array. I need to sum up the distance values with same 'driver' and 'date' keys
Input :
$firstArr = array(
0 =>array('driver'=>'xxxx',
'distance' => 100,
'vehicle' => 1,
'date' => '2019-10'),
1=>array('driver'=>'xxxx',
'distance' => 200,
'vehicle' => 2,
'date' => '2019-10'),
2=>array('driver'=>'yyyy',
'distance' => 100,
'vehicle' => 3,
'date' => '2019-10'));
Output Expected :
$finalArr = array(
0 =>array('driver'=>'xxxx',
'distance' => 300,
'vehicle' => '1,2',
'date' => '2019-10'),
1=>array('driver'=>'yyyy',
'distance' => 100,
'vehicle' => 3,
'date' => '2019-10'));
I tried below code. But the output is a datewise array.
$subArr = array();
foreach($firstArr as $key => $val){
$subArr[$val['driver']][$val['date']]['distance'] += $val['distance'];
$subArr[$val['driver']][$val['date']]['vehicle'] .= $val['vehicle'].',';
$subArr[$val['driver']][$val['date']]['date'] = $val['date'];
$subArr[$val['driver']][$val['date']]['driver'] = $val['driver'];
}
$result = array_values($subArr);

You can use the dirver and date as a key, Demo
$result = [];
foreach($array as $v){
$key = $v["driver"] . "_" . $v["date"];
if(isset($result[$key])){
$result[$key]["distance"] += $v["distance"];
}else{
$result[$key] = $v;
}
}
$result = array_values($result);

You can also use array_reduce to sum up the distance and the vehicles by driver and date.
With the help of array_combine we can set the initial array if not exists for each driver date combo and then do the processing of the required fields.
$keys = array_keys($input[0]);
$result = array_reduce($input, function ($sum, $row) use ($keys) {
$key = $row['driver'] . $row['date'];
$sum[$key] = $sum[$key] ?? array_combine($keys, [$row['driver'], null, null, $row['date']]);
$sum[$key]['distance'] += $row['distance'];
$sum[$key]['vehicle'] .= (($sum[$key]['vehicle']) ? ',': '' ) . $row['vehicle'];
return $sum;
}, []);
Results in:
Array
(
[xxxx2019-10] => Array
(
[driver] => xxxx
[distance] => 300
[vehicle] => 1,2
[date] => 2019-10
)
[yyyy2019-10] => Array
(
[driver] => yyyy
[distance] => 100
[vehicle] => 3
[date] => 2019-10
)
)

Related

PHP group array by date with interval

I have this array
Array
(
[0] => Array
(
[date] => 2020-10-07 10:49:48
[content] => 1
)
[1] => Array
(
[date] => 2020-10-08 13:49:48
[content] => 2
)
[2] => Array
(
[date] => 2020-10-08 13:50:03
[content] => 3
)
)
I want to get:
Array
(
[0] => Array
(
[date] => 2020-10-07 10:49:48
[content] => 1
)
[1] => Array
(
[date] => 2020-10-08 13:50:03
[content] => 2, 3
)
)
Like you see, if date between elements is less than hour (for example), content of both elements should be grouped with the greater date value. I just don't understand how to do it properly
I got this, but idk why i have extra element in array
foreach ($result as $key => $value) {
foreach ($result as $key2 => $value2) {
if (abs(strtotime($value['date_added']) - strtotime($value2['date_added'])) <= 3600 && $key != $key2) {
$result[$key]['content'] = $value['content'] . ',' . $value2['content'];
unset($result[$key2]);
}
}
$merged_array[] = $result[$key];
}
It is not clearly described which time differences should be used. The solution here always calculates the time differences to the highest date of the group.
The array will be sorted in descending order of the date. The highest date is set as $groupDate.
A new $result array is created with a foreach loop.
If the date difference to $groupDate is less than $intervalHour, then 'content' is accumulated. In the other case a new $groupDate is set. At the end the result array is sorted again according to increasing date.
$data = [
['date' => '2020-10-07 10:49:48', 'content' => 1],
['date' => '2020-10-08 13:49:48', 'content' => 2],
['date' => '2020-10-08 13:50:03', 'content' => 3],
['date' => '2020-10-08 14:50:04', 'content' => 4],
];
$intervalHour = 1.0;
usort($data,function($a,$b){return $b['date'] <=> $a['date'];});
$groupDate = date_create($data[0]['date']);
$content = $data[0]['content'];
$result = [$data[0]];
$k = 0;
foreach($data as $i => $row){
if($i == 0) continue;
$diff = date_create($row['date'])->diff($groupDate);
$diffHour = $diff->days * 24 + $diff->h + $diff->i/60 + $diff->s/3600;
if($diffHour < $intervalHour) {
$content = $row['content'].','.$content;
}
else {
++$k;
$groupDate = date_create($row['date']);
$result[$k]['date'] = $groupDate->format('Y-m-d H:i:s');
$content = $row['content'];
}
$result[$k]['content'] = $content;
}
usort($result,function($a,$b){return $a['date'] <=> $b['date'];});
echo '<pre>',var_export($result);
Output with $intervalHour = 1.0
array (
0 =>
array (
'date' => '2020-10-07 10:49:48',
'content' => 1,
),
1 =>
array (
'date' => '2020-10-08 13:50:03',
'content' => '2,3',
),
2 =>
array (
'date' => '2020-10-08 14:50:04',
'content' => 4,
),
)
Output with $intervalHour = 2.0
array (
0 =>
array (
'date' => '2020-10-07 10:49:48',
'content' => 1,
),
1 =>
array (
'date' => '2020-10-08 14:50:04',
'content' => '2,3,4',
),
)
The problem is that your unset command does not work. When you use a foreach on an array, a copy of that array will be created and iterated. If you want to iterate over an instance that you can change on the fly, you would need to pass an iterable object.
As an example, the following would output 12, despite the unset:
$x = [1, 2];
foreach ($x as $key => $value) {
echo $value;
unset($x[1]);
}
I think best approach is using OOP. But if you want to work only with forloops and arrays, it could be done like this:
$usedKeys = [];
$dateContent =[];
foreach ($result as $key => $value) {
if (in_array($key, $usedKeys)) {
continue;
}
$usedKeys[] = $key;
$dateContent[$key]['content'] = [$value['content']];
foreach ($result as $key2 => $value2) {
if (in_array($key2, $usedKeys)) {
continue;
}
if (abs(strtotime($value['date']) - strtotime($value2['date'])) <= 3600 && $key != $key2) {
$dateContent[$key]['content'][] = $value2['content'];
$usedKeys[] = $key2;
}
}
}
$dateContent = array_map(function ($value) {
return implode(',', $value['content']);
}, $dateContent);

PHP remove duplicates and sum of them from array based on 2 keys [duplicate]

I have this array :
$data = [
0 => [
'date' => '2018-09-12',
'department' => 12,
'country' => 14,
'total' => 12
],
1 => [
'date' => '2018-09-12',
'department' => 12,
'country' => 14,
'total' => 18
],
2 => [
'date' => '2018-09-12',
'department' => 12,
'country' => 15,
'total' => 10
]
];
The return should be :
$return = [
0 => [
'date' => '2018-09-12',
'department' => 12,
'country' => 14,
'total' => 30
],
1 => [
'date' => '2018-09-12',
'department' => 12,
'country' => 15,
'total' => 10
]
];
I tried like this :
foreach ($data as $value) {
if(!in_array($value, $data)) {
$result[] = $data;
}
}
The idea is, if all fields except total are indentical, then add total to the existing total with the same fields. Please help me. Thx in advance and sorry for my english
You can do this by looping through your array, comparing all the other values of each element (date, department and country) with previously seen values and summing the totals when you get a match. This code uses serialize to generate a composite key of the other values for comparison:
$output = array();
$keys = array();
foreach ($data as $value) {
$total = $value['total'];
unset($value['total']);
$key = serialize($value);
if (($k = array_search($key, $keys)) !== false) {
$output[$k]['total'] += $total;
}
else {
$keys[] = $key;
$output[] = array_merge($value, array('total' => $total));
}
}
print_r($output);
Output:
Array (
[0] => Array (
[date] => 2018-09-12
[department] => 12
[country] => 14
[total] => 30
)
[1] => Array (
[date] => 2018-09-12
[department] => 12
[country] => 15
[total] => 10
)
)
Demo on 3v4l.org
By using the composite key as the index into the $output array, we can simplify this code, we just need to use array_values after the loop to re-index the $output array:
$output = array();
foreach ($data as $value) {
$v = $value;
unset($v['total']);
$key = serialize($v);
if (isset($output[$key])) {
$output[$key]['total'] += $value['total'];
}
else {
$output[$key] = $value;
}
}
$output = array_values($output);
print_r($output);
Output is the same as before. Demo on 3v4l.org
You can also try this method as it doesn't involve much memory intensive operations such as merging, especially while working with large amount of data:
$new_data=[];
foreach($data as $key=>$value){
$i=array_search($value["date"],array_column($new_data,"date"));
if(($i!==false)&&($new_data[$i]["department"]==$value["department"])&&($new_data[$i]["country"]==$value["country"])){
$new_data[$i]["total"]+=$value["total"];
}
else{
$new_data[]=$value;
}
}
print_r($new_data);

Search for duplicates, if found sum values

I have this array :
$data = [
0 => [
'date' => '2018-09-12',
'department' => 12,
'country' => 14,
'total' => 12
],
1 => [
'date' => '2018-09-12',
'department' => 12,
'country' => 14,
'total' => 18
],
2 => [
'date' => '2018-09-12',
'department' => 12,
'country' => 15,
'total' => 10
]
];
The return should be :
$return = [
0 => [
'date' => '2018-09-12',
'department' => 12,
'country' => 14,
'total' => 30
],
1 => [
'date' => '2018-09-12',
'department' => 12,
'country' => 15,
'total' => 10
]
];
I tried like this :
foreach ($data as $value) {
if(!in_array($value, $data)) {
$result[] = $data;
}
}
The idea is, if all fields except total are indentical, then add total to the existing total with the same fields. Please help me. Thx in advance and sorry for my english
You can do this by looping through your array, comparing all the other values of each element (date, department and country) with previously seen values and summing the totals when you get a match. This code uses serialize to generate a composite key of the other values for comparison:
$output = array();
$keys = array();
foreach ($data as $value) {
$total = $value['total'];
unset($value['total']);
$key = serialize($value);
if (($k = array_search($key, $keys)) !== false) {
$output[$k]['total'] += $total;
}
else {
$keys[] = $key;
$output[] = array_merge($value, array('total' => $total));
}
}
print_r($output);
Output:
Array (
[0] => Array (
[date] => 2018-09-12
[department] => 12
[country] => 14
[total] => 30
)
[1] => Array (
[date] => 2018-09-12
[department] => 12
[country] => 15
[total] => 10
)
)
Demo on 3v4l.org
By using the composite key as the index into the $output array, we can simplify this code, we just need to use array_values after the loop to re-index the $output array:
$output = array();
foreach ($data as $value) {
$v = $value;
unset($v['total']);
$key = serialize($v);
if (isset($output[$key])) {
$output[$key]['total'] += $value['total'];
}
else {
$output[$key] = $value;
}
}
$output = array_values($output);
print_r($output);
Output is the same as before. Demo on 3v4l.org
You can also try this method as it doesn't involve much memory intensive operations such as merging, especially while working with large amount of data:
$new_data=[];
foreach($data as $key=>$value){
$i=array_search($value["date"],array_column($new_data,"date"));
if(($i!==false)&&($new_data[$i]["department"]==$value["department"])&&($new_data[$i]["country"]==$value["country"])){
$new_data[$i]["total"]+=$value["total"];
}
else{
$new_data[]=$value;
}
}
print_r($new_data);

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

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