PHP: substract values of 2 arrays of different length - php

Well, I've got two following arrays:
$incomesArr = [
['1605564000' => 121],
['1605736800' => 3.50],
];
$expensesArr = [
['1605736800' => 3.50],
['1605736800' => 3.50],
['1605564000' => 28],
['1605936000' => 50]
];
As you can see from the variable names, these are the expenses and incomes for the current date (it is a timestamp). All I want to do is to generate the "Revenue" data by substracting value of $expensesArr from $incomesArr with corresponding date, and if there is no date, then substract from 0. Also, if there is no corresponding date in the $expensesArr, then substract nothing (or 0).
Basically, I want to achieve the following result:
['1605564000', 93]; // 121 - 28 = 93
['1605736800', -3.50] // 3.50 - 3.50 - 3.50 (since both expenses are for the same date)
['1605936000', -50] // 0 - 50, since there is no income for that day.
Please note, that the output result should be in the ['key', 'value'] format, not the ['key' => 'value'], since I send these data to jQuery Flot (displaying graphs).
I carry about the performance, since there may be kind of significant amount of data and the best way for me to achieve this is without foreach loop, however if it is the only possible option, then foreach it is!
Any help is highly appreciated. Thank you in advance!
P.S. If it would help, then I do this in Laravel, however I don't think that there is a special helper exactly for that, since I already examined the Laravel documentation multiple times :)
My previous attempt was a bit confusing and scary, but here it is:
$newArr = array_merge([$incomesArr, $expensesArr]);
foreach ($incomesArr as $k => $v){
$sub = $v - $expensesArr[$k];
// this is like : array1[1]-array2[1]
}
$finalArr = [];
foreach($newArr as $key => $value) {
var_dump($value);
foreach ($value as $key => $val) {
$finalArr[$key] = ($val[$key] ?? 0) - ($val[$key] ?? 0);
}
}

$incomesArr = [
['1605564000' => 121],
['1605736800' => 3.50],
];
$expensesArr = [
['1605736800' => 3.50],
['1605736800' => 3.50],
['1605564000' => 28],
['1605936000' => 50]
];
// get data
$dataArr = [];
foreach([-1 => $expensesArr, 1 => $incomesArr] as $ratio => $list){
foreach($list as $value){
$time = key($value);
$dataArr[$time] = ($dataArr[$time] ?? 0) + ($value[$time] * $ratio);
}
}
// convert to your format
$finalArr = [];
foreach($dataArr as $time => $value){
$finalArr []= [$time, $value];
}
print_r($finalArr);
Output
Array
(
[0] => Array
(
[0] => 1605736800
[1] => -3.5
)
[1] => Array
(
[0] => 1605564000
[1] => 93
)
[2] => Array
(
[0] => 1605936000
[1] => -50
)
)

I would split it into two functions like so:
$incomesArr = [
['1605564000' => 121],
['1605736800' => 3.50],
];
$expensesArr = [
['1605736800' => 3.50],
['1605736800' => 3.50],
['1605564000' => 28],
['1605936000' => 50]
];
function getTotalByKey($inputArray, $inputKey)
{
$sum = 0;
foreach ($inputArray as $subArray)
foreach ($subArray as $key => $value) {
$sum += $key === $inputKey ? $value : 0;
}
return $sum;
}
function getProfitByKey($incomesArray, $expensesArray, $key){
return getTotalByKey($incomesArray, $key) - getTotalByKey($expensesArray, $key);
}
$profit = getProfitByKey($incomesArr, $expensesArr, 1605736800); // Outputs -3.5

$incomesArr = [
['1605564000' => 121],
['1605736800' => 3.50],
];
$expensesArr = [
['1605736800' => 3.50],
['1605736800' => 3.50],
['1605564000' => 28],
['1605936000' => 50]
];
$tmp = [];
$final = [];
foreach ($incomesArr as $pair)
{
foreach ($pair as $date => $amount)
{
$tmp[$date] = ($tmp[$date] ?? 0) + $amount;
}
}
foreach ($expensesArr as $pair)
{
foreach ($pair as $date => $amount)
{
$tmp[$date] = ($tmp[$date] ?? 0) - $amount;
}
}
print_r($tmp);
foreach ($tmp as $date => $amount)
{
$final[] = [$date, $amount];
}
print_r($final);
Output
Array
(
[1605564000] => 93
[1605736800] => -3.5
[1605936000] => -50
)
Array
(
[0] => Array
(
[0] => 1605564000
[1] => 93
)
[1] => Array
(
[0] => 1605736800
[1] => -3.5
)
[2] => Array
(
[0] => 1605936000
[1] => -50
)
)

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);

how to sum string values which are coming in array

Here is my array
Array
(
[0] => Array
(
[Asan_Name_Val] => 447
[Actual_Ratio] => 15/00/15,04/05/05
)
[1] => Array
(
[Asan_Name_Val] => 447
[Actual_Ratio] => 10/05/11,00/06/05
)
)
The actual ratio value should sum with it values like 15+10=15,00+05=05,15+11=26 .....
So The desired output I want In this format
Array
(
[Asan_Name_Val] => 447
[Actual_Ratio] => 25/05/26,04/11/10
)
Wrote a hacky solution in PHP.
Will work but strongly advice you to optimize.
<?php
$myArray = array(
0 => array(
'Asan_Name_Val' => '447',
'Actual_Ratio' => '15/00/15,04/05/05'
),
1 => array(
'Asan_Name_Val' => '447',
'Actual_Ratio' => '10/05/11,00/06/05'
)
);
$sums = array_fill(0,6,'0');
foreach($myArray as $arr){
$ratio = $arr['Actual_Ratio'];
$j=0;
for($i=0;$i<6;$i++){
$sums[$i] = sprintf("%02d", $sums[$i]+substr($ratio,$j,2));
$j = $j+3;
}
}
$finalRatio = "$sums[0]/$sums[1]/$sums[2],$sums[3]/$sums[4]/$sums[5]";
$desiredArray['Asan_Name_Val'] = '447';
$desiredArray['Actual_Ratio'] = $finalRatio;
print_r($desiredArray);
This would be a flexible and clean approach:
<?php
$input = [
[
'Asan_Name_Val' => "447",
'Actual_Ratio' => "15/00/15,04/05/05"
],
[
'Asan_Name_Val' => "447",
'Actual_Ratio' => "10/05/11,00/06/05"
]
];
$output = [];
$formatter = new NumberFormatter('de_DE', NumberFormatter::DECIMAL);
array_walk($input, function($entry) use (&$output, $formatter) {
$values = explode("/", $entry['Actual_Ratio']);
foreach($values as &$value) {
$value = $formatter->parse($value);
}
$output[$entry['Asan_Name_Val']][] = $values;
});
array_walk($output, function(&$entry, $key) use ($formatter) {
for ($i = 0; $i < count($entry[0]); $i++) {
$sums[] = $formatter->format(array_sum(array_column($entry, $i)));
}
$entry = [
'Asan_Name_Val' => $key,
'Actual_Ratio' => implode("/", $sums)
];
});
print_r(array_values($output));
The output obviously is:
Array
(
[0] => Array
(
[Asan_Name_Val] => 447
[Actual_Ratio] => 25/5/26,04/11/10
)
)

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 create unique array from array

Array
[0] => Array
(
[a1] => 12
[v1] => 3100.00
[v2] => 186.00
[v3] => 186.00
)
[1] => Array
(
[a1] => 12
[v1] => 1200.00
[v2] => 72.00
[v3] => 72.00
)
i want to create new array from this array
which is look like this as given below it should give me '12' common and add other values
Array
[0] => Array
(
[a1] => 12
[v1] => 4300.00
[v2] => 258.00
[v3] => 258.00
)
Try This code ,
foreach($value as $i=>$v) {
$temp[0]['a1'] = $v['a1'];
$temp[0]['v1'] += $v['v1'];
$temp[0]['v2'] += $v['v2'];
$temp[0]['v3'] += $v['v3'];
}
Please find below code:
<?php
$merged = array();
$res = array('a1'=>12,'v1'=>3100.00,'v2'=>186.00,'v3'=>186.00);
$res1 = array('a1'=>12,'v1'=>1200.00,'v2'=>72.00,'v3'=>72.00);
foreach ([$res, $res1] as $a) { // iterate both arrays
foreach ($a as $key => $value) { // iterate all keys+values
$merged[$key] = $value + (isset($merged[$key]) ? $merged[$key] : 0); // merge and add
}
}
print "<pre>";
print_r($merged);
die;
?>
It's a bit unclear the real your needs, so this solution only one of a few possible solutions.
This script a1 element is using as key, so it will work with more than one a1 value.
Example:
<?php
$a = [
[
'a1' => 12,
'v1' => 3100.00,
'v2' => 186.00,
'v3' => 186.00,
],
[
'a1' => 12,
'v1' => 1200.00,
'v2' => 72.00,
'v3' => 72.00,
],
[
'a1' => 13,
'v1' => 2100.00,
'v2' => 386.00,
'v3' => 386.00,
],
[
'a1' => 13,
'v1' => 1200.00,
'v2' => 72.00,
'v3' => 72.00,
]
];
$r = [];
foreach ($a as $item) {
$key = $item['a1'];
if (empty($r[$key]))
$r[$key] = $item;
else {
foreach ($item as $k => $v) {
if ($k !== 'a1')
$r[$key][$k] = empty($r[$key][$k]) ? $item[$k] : $r[$key][$k] + $item[$k];
}
}
}
print_r(array_values($r));
A simple foreach loop can do it.
$result = array();
foreach($yourArray as $arr){
foreach($arr as $i=>$v) {
if(!isset($result[$i])) {
$result[$i] = 0;
}
if($i == 'a1'){
$result[$i] = $v;
} else {
$result[$i] += $v;
}
}
}

Categories