Home > Software engineering >  PHP split money from arrays with positive and negative values
PHP split money from arrays with positive and negative values

Time:04-07

I'm currently working on a calculator which splits up money. For example my friends and I go on vacation for 5 days. Everybody pays something for the group but not everybody is there the full 5 days. One friend is only two days there, another one is three days, another two are 4 days and the rest is there everyday. I already got to the part where I know who gets how much money and who needs to pay how much money. Now I "only" need to figure out who needs to pay who with the least transactions. I have an array of positive numbers:

40 => 68.25($)
42 => 44.25($)
49 => 28.75($)
41 => 24.25($)

and an array of negative numbers:

50 => -80.5($)
48 => -57.5($)
43 => -27.5($)

The key of these arrays are just and id's. So I know which value belongs to which friend. The positive numbers should get payed and the negative numbers need to pay. If you sum up the values from each array you get the same result except the sum of the negative array is negative, obviously.

My question now is how do I get the transactions so that in the end everybody gets or pays the right amount?

Thanks in advance for your ideas.

CodePudding user response:

In my opinion, it is better to change your data structure, since it is only readable by you or the person who enters those, what I am proposing to use is to change your current structure to something like this:

$transactions = [
    [
        "day" => "1",
        "made by" => "Awat",
        "cost" => "68.25"
    ],...
]

if you take a look or this, now everyone can read and have a better understanding and you also can calculate a better way, since now you can get access to any information you need by day or who has made this transaction?

so, for a better understanding of this, I have created a simple calculation for you, please keep in mind I have created this sample in the simplest way hope you understand it all, let me know if you have some difficulties,

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Stack25</title>
</head>
<body>


<?php

$transactions = [
    [
        "day" => "1",
        "made by" => "Awat",
        "cost" => "68.25"
    ],
    [
        "day" => "2",
        "made by" => "Chitter",
        "cost" => "44.25"
    ],
    [
        "day" => "3",
        "made by" => "Awat",
        "cost" => "28.75"
    ],
    [
        "day" => "4",
        "made by" => "Chitter",
        "cost" => "24.25"
    ],
    [
        "day" => "5",
        "made by" => "Chitter",
        "cost" => "-80.5"
    ],
    [
        "day" => "6",
        "made by" => "Awat",
        "cost" => "-57.5"
    ],
    [
        "day" => "7",
        "made by" => "Awat",
        "cost" => "-27.5"
    ]
];


echo "<pre>The structure of my array:\n\n";
print_r($transactions);
echo "</pre>";


$totalCostP = 0;
$totalCostN = 0;

$awatCostP = 0;
$awatCostN = 0;

$chitterCostP = 0;
$chitterCostN = 0;

foreach ($transactions as $key => $value) {

    // Cal
    if ($value['made by'] === "Awat"){

        // IS IT SPEND OR GAIN?
        // - = SPEND
        //   = GAIN
        if ($value['cost'] > 0){
            $awatCostP  = floatval($value['cost']);
            $totalCostP  = floatval($value['cost']);
        }else{
            $awatCostN  = floatval($value['cost']);
            $totalCostN  = floatval($value['cost']);
        }

    }

    if ($value['made by'] === "Chitter"){
        if ($value['cost'] > 0){
            $chitterCostP  = floatval($value['cost']);
            $totalCostP  = floatval($value['cost']);
        }else{
            $chitterCostN  = floatval($value['cost']);
            $totalCostN  = floatval($value['cost']);
        }
    }

}


echo "<h1>Awat has positive Balance: ".$awatCostP."</h1>";
echo "<h1>Awat has negative Balance: ".$awatCostN."</h1>";
echo "<h1>Awat total Balance: ".($awatCostP - ($awatCostN * -1))."</h1>";

echo "<hr>";

echo "<h1>Chitter has positive Balance: ".$chitterCostP."</h1>";
echo "<h1>Chitter has negative Balance: ".$chitterCostN."</h1>";
echo "<h1>Chitter total Balance: ".($chitterCostP - ($chitterCostN * -1))."</h1>";

echo "<hr>";

echo "<h1>Total positive Balance: ".$totalCostP."</h1>";
echo "<h1>Total negative Balance: ".$totalCostN."</h1>";

?>

</body>
</html>

My final result

Note: last note is, if you your data is large, I am not recommending using array, it is much better to store in Database

CodePudding user response:

You can do it pragmatically with a loop.

<?php
$a1 = [2 => 10, 1 => 5];
$a2 = [2 => 9, 1=> -2];
$a3 = $a1;

foreach($a2 as $k => $v) {
    if(array_key_exists($k, $a3)) {
       $a3[$k]  = $v;
    } else {
       $a3[$k] = $v; 
    }
}

print_r($a3);
exit();

output

Array
(
    [2] => 19
    [1] => 3
)

CodePudding user response:

I figured it out for everyone who is interested.

$transactions = [];

try {
  foreach ($positives as $positive_participant_id => $positive) {
    foreach ($negatives as $negative_participant_id => $negative) {
      $rest = $positive - $negative * -1;

      if ($rest == 0) {
        // In this case the positive has received enough and negative has paid everything.
        $this->addTransaction($transactions, $negative_participant_id, $positive_participant_id, $positive);
        unset($positives[$positive_participant_id]);
        unset($negatives[$negative_participant_id]);
      }
      elseif ($rest < 0) {
        // In this case the positive has received enough.
        $this->addTransaction($transactions, $negative_participant_id, $positive_participant_id, $positive);
        $negatives[$negative_participant_id] = $rest;
        unset($positives[$positive_participant_id]);
        break;
      }
      elseif ($rest > 0) {
        // In this case the positive has not received enough but negative has paid everything.
        $this->addTransaction($transactions, $negative_participant_id, $positive_participant_id, $negative * -1);
        $positives[$positive_participant_id] = $rest;
        $positive = $rest;
        unset($negatives[$negative_participant_id]);
      }
      else {
        throw new \UnexpectedValueException("Rest value ($rest) in transaction calculation is an unexpected value.");
      }
    }
  }
}
catch (\UnexpectedValueException $e) {
  $this->logger->error($e->getMessage());
}

The unset in the elseif ($rest < 0) is unnecessary, but when debugging it helped me see how many positives are left. The transactions array has more information about the participant (name, days and price). I'm not sure if this solution has the least transactions but it works for me. It's also not the prettiest solution. So if you have improvements let me know.

private function addTransaction(array &$transactions, $sender, $receiver, $amount) {
  $transactions[] = [
    'sender' => [
      'participant' => $this->calculatorStorage->getParticipantById($sender),
      'price' => $amount,
    ],
    'receiver' => [
      'participant' => $this->calculatorStorage->getParticipantById($receiver),
    ],
  ];
}

If you want to see how I calculated the price each participant needs to pay per day, let me know. I can explain it further if needed.

  • Related