
本文详解如何将键为区间、值为数值的一维关联数组,按相邻相同值自动合并为连续区间对象,生成符合 JSON 存储规范的结构化数组,适用于费率表、阶梯定价等业务场景。
本文详解如何将键为区间、值为数值的一维关联数组,按**相邻相同值自动合并为连续区间对象**,生成符合 json 存储规范的结构化数组,适用于费率表、阶梯定价等业务场景。
在处理 Excel 导入的阶梯式配置数据(如“0-300|300-500|500-1000|1000-5000|>5000”对应数值行)时,常见需求是:将连续相同数值的区间合并为一个逻辑区间对象,避免冗余存储,同时保证上下界(lb/ub)准确衔接。例如 ['0-300'=>2.5, '300-500'=>2.5, '500-1000'=>2.5] 应合并为 [{'lb':'0','ub':'1000','value':2.5}];而遇到值变化(如后续为 2.4),则开启新区间。
该逻辑本质是按值连续性进行分组(run-length encoding 的变体),而非简单去重或统计频次。核心在于:
✅ 遍历有序键值对(PHP 关联数组保持插入顺序);
✅ 解析每个键为下界(lb)和上界(ub),特殊处理 >X 格式;
✅ 维护当前运行区间的起始 lb 与动态扩展的 ub;
✅ 值变更时,将当前区间推入结果,并重置新区间起点。
以下为完整、健壮、可复用的实现方案:
✅ 核心函数实现
/**
* 解析区间键(如 "0-300" 或 ">5000")为 [lower, upper]
*/
function parseBand(string $key): array
{
if (str_starts_with($key, '>')) {
$lower = ltrim($key, '>');
return [$lower, 'INF'];
}
[$lower, $upper] = explode('-', $key, 2);
return [$lower, $upper];
}
/**
* 将单行区间数值数组转换为合并后的区间对象数组
* @param array<string, float|int> $slabs 键为区间字符串,值为数值
* @return array<object> 每个元素含 lb, ub, value 属性的对象
*/
function convertSlabs(array $slabs): array
{
if (empty($slabs)) {
return [];
}
$result = [];
$keys = array_keys($slabs);
$values = array_values($slabs);
// 初始化第一个区间
[$lb, $ub] = parseBand($keys[0]);
$currentValue = $values[0];
// 从第二个元素开始遍历
for ($i = 1; $i < count($keys); $i++) {
[$nextLb, $nextUb] = parseBand($keys[$i]);
$nextValue = $values[$i];
if ($nextValue == $currentValue) {
// 值相同 → 扩展当前区间上界
$ub = $nextUb;
} else {
// 值不同 → 提交当前区间,开启新区间
$result[] = (object)[
'lb' => $lb,
'ub' => $ub,
'value' => $currentValue
];
$lb = $nextLb;
$ub = $nextUb;
$currentValue = $nextValue;
}
}
// 提交最后一个区间
$result[] = (object)[
'lb' => $lb,
'ub' => $ub,
'value' => $currentValue
];
return $result;
}✅ 使用示例与验证
$testData = [
['0-300' => 2.5, '300-500' => 2.5, '500-1000' => 2.5, '1000-5000' => 2.4, '>5000' => 2.4],
['0-300' => 1.2, '300-500' => 1.2, '500-1000' => 1.2, '1000-5000' => 1.3, '>5000' => 1.4],
['0-300' => 2.5, '300-500' => 2.5, '500-1000' => 2.5, '1000-5000' => 2.5, '>5000' => 2.5],
];
foreach ($testData as $i => $row) {
$json = json_encode(convertSlabs($row), JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
echo "第 {$i} 行结果:\n{$json}\n\n";
}输出示例:
[
{"lb":"0","ub":"1000","value":2.5},
{"lb":"1000","ub":"INF","value":2.4}
]⚠️ 注意事项与最佳实践
- 顺序敏感:PHP 关联数组默认保留插入顺序,但务必确保 Excel 解析后键顺序与原始列序一致(如使用 PhpSpreadsheet 时注意读取模式);
- 边界一致性:相邻区间必须首尾相接(如 "300-500" 后接 "500-1000"),否则合并逻辑将产生语义错误;
-
特殊键容错:parseBand() 已支持 >N 格式,但不支持 >=N、
- 性能考量:单行处理时间复杂度为 O(n),适用于千级以内区间;超大规模建议预检并分批处理;
- JSON 存储建议:入库前使用 json_encode(..., JSON_INVALID_UTF8_IGNORE) 防止非法字符导致失败(尤其来自 Excel 的不可见字符)。
此方案已通过多组边界用例验证(含全同值、单值、交替值、首尾突变等),可直接集成至 Laravel、Symfony 或原生 PHP 数据导入模块中,为阶梯计费、阈值规则、分段配置等场景提供稳定、清晰的数据结构支撑。
立即学习“PHP免费学习笔记(深入)”;



















