PHP实现数组递归扣减逻辑求助:金额清零及扣减额转移需求
实现递归扣减金额的PHP逻辑
数据说明
原始数据如下:
# amount deduction [0] 50 10 [1] 160 20 [2] 300 20
需求说明
对数组执行递归扣减操作,直至所有金额变为0;当数组中某一项金额为0时,需将其对应的deduction(扣减额)追加至下一项的扣减额中继续扣减,直至所有金额均为0。
计算示例
[0] [1] [2] 50 - 10 160 - 20 300 - 20 40 - 10 140 - 20 280 - 20 30 - 10 120 - 20 260 - 20 20 - 10 100 - 20 240 - 20 10 - 10 80 - 20 220 - 20 0 60 - 30 200 - 20 30 - 30 180 - 20 0 160 - 50 110 - 50 60 - 50 10 - 50 0
当前编写的PHP代码
foreach($data as $det) { $_monthly_payment = $det->deduction; $_beginning_balance = $det->amount; $i = 0; do{ $_new_balance = (($i + 1) == 1) ? $_beginning_balance : $_ending_balance; $_toward_principal = $_monthly_payment; $_ending_balance = $_new_balance - $_toward_principal; <tr> <td><?php echo number_format($_monthly_payment,2); ?></td> <td>$<?php echo number_format($_new_balance,2); ?> </td> <td>$<?php echo number_format($_toward_principal,2); ?> </td> <td>$<?php echo number_format($_ending_balance,2); ?> </td> </tr> } while( $i <= $_ending_balance ); }
集合数据结构(var_dump输出)
object(Illuminate\Support\Collection)#1334 (1) { ["items":protected]=> array(3) { [0]=> object(stdClass)#1341 (26) { ["id"]=> int(130) ["account_name"]=> string(3) "QWE" ["amount"]=> float(50) ["deduction"]=> float(10) ["updated_at"]=> string(19) "2023-01-09 15:49:59" } [1]=> object(stdClass)#1345 (26) { ["id"]=> int(131) ["account_name"]=> string(3) "ABC" ["amount"]=> float(160) ["deduction"]=> float(20) ["updated_at"]=> string(19) "2023-01-09 15:49:59" } [2]=> object(stdClass)#1326 (26) { ["id"]=> int(132) ["account_name"]=> string(3) "XYZ" ["amount"]=> float(300) ["deduction"]=> float(20) ["updated_at"]=> string(19) "2023-01-09 15:49:59" } } }
解决方案代码
以下是实现需求的PHP代码,基于Illuminate集合处理:
// 复制原集合数据,避免修改原始数据,添加状态标记 $processingItems = $data->map(function($item) { return (object)[ 'account_name' => $item->account_name, 'amount' => $item->amount, 'deduction' => $item->deduction, 'isCompleted' => false // 标记当前项是否已清零 ]; })->toArray(); $stepRecords = []; // 记录每一轮的扣减操作 // 循环处理直到所有项都清零 while (true) { $allCompleted = true; $currentStep = []; foreach ($processingItems as $index => &$item) { if ($item->isCompleted) { $currentStep[$index] = [ 'original' => 0, 'deduction' => 0, 'remaining' => 0 ]; continue; } $allCompleted = false; // 计算本次扣减额,最后一步直接扣完剩余金额 $deductAmount = min($item->deduction, $item->amount); $remainingAmount = $item->amount - $deductAmount; $currentStep[$index] = [ 'original' => $item->amount, 'deduction' => $deductAmount, 'remaining' => $remainingAmount ]; $item->amount = $remainingAmount; // 如果当前项已清零,将其扣减额追加到下一项 if ($remainingAmount === 0) { $item->isCompleted = true; if (isset($processingItems[$index + 1])) { $processingItems[$index + 1]->deduction += $item->deduction; } } } // 所有项都完成则退出循环 if ($allCompleted) { break; } $stepRecords[] = $currentStep; } // 输出符合示例格式的表格 echo '<table border="1">'; // 表头:显示账户名称 echo '<tr>'; foreach ($processingItems as $item) { echo "<th>{$item->account_name}</th>"; } echo '</tr>'; // 输出每一步的扣减记录 foreach ($stepRecords as $step) { echo '<tr>'; foreach ($step as $col) { if ($col['original'] === 0) { echo '<td>0</td>'; } else { echo "<td>{$col['original']} - {$col['deduction']}</td>"; } } echo '</tr>'; } // 最后一行显示全0状态 echo '<tr>'; foreach ($processingItems as $item) { echo '<td>0</td>'; } echo '</tr>'; echo '</table>';
代码说明
- 数据复制与标记:复制原集合数据并添加
isCompleted标记,防止修改原始数据,同时跟踪项是否已清零。 - 循环扣减逻辑:每一轮遍历所有未完成的项,执行扣减操作;最后一步直接扣完剩余金额,避免出现负余额。
- 扣减额转移:当某一项清零后,将其原始扣减额追加到下一项的扣减额中,实现需求中的累加逻辑。
- 记录与输出:每一步的扣减操作都会被记录,最后按照示例格式输出表格,清晰展示每一轮的变化。
内容的提问来源于stack exchange,提问作者angelloucelle
相关产品推荐
相关产品推荐

