人性化递归替换数组


背景

有一套通用位置锁定服务,资源可以是任意的。前台通过锁定服务拿到锁定资源信息,锁定数据由后台写入。

需求

现在有一个需求,可以在锁定的时候,修改资源的一些信息。

解读

  1. 不同资源都有自身的数据格式,后台肯定需要列表资源格式,才能写入正确的复写内容。
  2. 后台复写的内容肯定需要与原数据格式相同。
  3. 后台只允许写入原数据中存在的数据,原数据中不存在的字段,不允许新增写入。
  4. 对于数组的处理需要区分纯数字数组和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 条评论

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注