背景
有一套通用位置锁定服务,资源可以是任意的。前台通过锁定服务拿到锁定资源信息,锁定数据由后台写入。
需求
现在有一个需求,可以在锁定的时候,修改资源的一些信息。
解读
- 不同资源都有自身的数据格式,后台肯定需要列表资源格式,才能写入正确的复写内容。
- 后台复写的内容肯定需要与原数据格式相同。
- 后台只允许写入原数据中存在的数据,原数据中不存在的字段,不允许新增写入。
- 对于数组的处理需要区分纯数字数组和kv数组,纯数字数组不需要计算每个索引的差异,只需要整体复写即可。
举例
例如资源数据格式:
$a = [
'foo' => 123,
'bar' => [
1,2,3,9
],
'zoo' => [
'x' => 8,
'y' => 9,
'a' => 666
],
'k' => [
'kk' => [
'kkk' => 1,
'kkkk' => 2
]
],
'hehe' => true
];
复写数据(为了演示效果,特意加不应该存在的内容):
$b = [
'foo' => 123456,
'bar' => [
3,4,5
],
'zoo' => [
'x' => 88,
'y' => 99,
'z' => 100
],
'k' => [
'kk' => [
'kkk' => 2,
'xxxxx' => '醋'
]
],
'no' => '酱油'
];
结果(仔细对比上边需求中的几条规则):
array (
'foo' => 123456,
'bar' =>
array (
0 => 3,
1 => 4,
2 => 5,
),
'zoo' =>
array (
'x' => 88,
'y' => 99,
'a' => 666,
),
'k' =>
array (
'kk' =>
array (
'kkk' => 2,
'kkkk' => 2,
),
),
'hehe' => true,
)
实现
首先,数组合并就会想到array_merge,实际上这个函数是无法满足需求的。它并没有递归的进行比对。在第一层key发现相同的时候,就会用后边的替换前边的。当然,如果你说还有array_merge_recursive呢,它会把同样key的value可并成一个数组。。。。 也就是原本是数字的会合并成数组。具体自己官方文档。
array_replace,array_replace_recursive同样的也都无法满足需求。
自行编写函数实现其实挺简单:
/**
* 比对数据类型,使用提供数组覆盖原数组
* @param array $array 原数组
* @param array $replace 覆盖数组,此数组中的值如果在原数组中,则替换掉原数组的值
* @return array
*/
function rewriteArray(array $array, array $replace): array{
array_walk($array, 'compareTypeAndReplace', $replace);
return $array;
}
function compareTypeAndReplace(&$v, $k, $replace){
if (is_array($v) && array_keys($v) !== range(0, count($v) - 1)){
isset($replace[$k]) && array_walk($v, __FUNCTION__, $replace[$k]);
}else{
if (isset($replace[$k]) && gettype($replace[$k]) === gettype($v)){
$v = $replace[$k];
}
}
}
var_export(rewriteArray($a, $b));
补充个单元测试(以静态类的方式重构了)
<?php
class ArrayHelperTest extends \PHPUnit\Framework\TestCase
{
function testRewriteArray()
{
$a = [
'foo' => 123,
'bar' => [
1, 2, 3, 9
],
'zoo' => [
'x' => 8,
'y' => 9,
'a' => 666
],
'k' => [
'kk' => [
'kkk' => 1,
'kkkk' => 2
]
],
'hehe' => true
];
$b = [
'foo' => 123456,
'bar' => [
3, 4, 5
],
'zoo' => [
'x' => 88,
'y' => 99,
'z' => 100
],
'k' => [
'kk' => [
'kkk' => 2,
'xxxxx' => '醋'
]
],
'no' => '酱油'
];
$result = ArrayHelper::rewriteArray($a, $b);
$expected = array(
'foo' => 123456, // 数字类型直接替换
'bar' => // 自然数组直接替换
array(
0 => 3,
1 => 4,
2 => 5,
),
'zoo' => // KV数组,按照key进行对比替换,不存在的key不写入(z=100没有写入)
array(
'x' => 88,
'y' => 99,
'a' => 666,
),
'k' => // 多维KV数组,递归按照key进行对比替换,不存在的key同样不写入
array(
'kk' =>
array(
'kkk' => 2,
'kkkk' => 2,
),
),
'hehe' => true,
);
$this->assertEquals($expected, $result);
}
}
《“人性化递归替换数组”》 有 1 条评论
没看懂。。。