背景
有一套通用位置锁定服务,资源可以是任意的。前台通过锁定服务拿到锁定资源信息,锁定数据由后台写入。
需求
现在有一个需求,可以在锁定的时候,修改资源的一些信息。
解读
- 不同资源都有自身的数据格式,后台肯定需要列表资源格式,才能写入正确的复写内容。
- 后台复写的内容肯定需要与原数据格式相同。
- 后台只允许写入原数据中存在的数据,原数据中不存在的字段,不允许新增写入。
- 对于数组的处理需要区分纯数字数组和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 条评论
没看懂。。。