使用带有部分键的数组的映射数组合并关联数组(支持命名参数)

时间:2014-08-27 03:49:16

标签: php arrays

没有详细介绍,这是我正在开发的模板系统,专门用于支持模板过滤器和函数中的命名参数。

我将尽力使用我的escapeHtml过滤器来解决问题,在本机PHP中基本上是htmlspecialchars

考虑一下,我有这个映射数组(这会根据被激活的函数或过滤器而改变)

$mapping = array(
    'string' => array(
        'type' => 'string'
    ),
    'flags' => array(
        'type' => 'integer',
        'default' => 11
    ),
    'encoding' => array(
        'type' => 'string',
        'default' => 'UTF-8'
    ),
    'double_encode' => array(
        'type' => 'boolean',
        'default' => true
    )
);

为了解释这一点,第一级密钥(如flags)是输入名称,type是支持的输入类型(这对此问题并不重要)。如果输入中没有提供,default只是默认值。现在我正在开发的代码允许我必须映射到此数组的一些输入,输入可以索引(数字)或使用与上面的输入名称匹配的键。映射数组的索引输入的顺序正确或预期。

所以输入的一些例子,

$input = array(
    'string' => 'hello',
     0 => 'ISO-8859-1',
    1 => false,
    'flags' => 3
 );

现在我需要将它与映射数组进行协调,这样输出就是这个,

$output = array(
    'string' => 'hello',
    'flags' => 3
    'encoding' => 'ISO-8859-1',
    'double_encode' => false,
);

因此,如果你到目前为止,我需要带有键的项目来匹配映射数组,并且任何剩余的元素(数字索引)将按它们出现的顺序添加。有一些错误检查涉及但我不是此刻过于担心。

我确实有这个工作,但它花了我大约70行代码和3个循环,所以如果你能打败它作为一个额外的奖励你在项目源代码:)得到它的信誉。我还没有添加任何代码(目前还不稳定)但是Git Repo就在这里:

https://github.com/ArtisiticPhoenix/Jet/

那就是说,我在自述文件和维基中添加了很多东西。这里的这一点对于这个问题并不重要,但应提供一些内容。在我的模板系统中,过滤器以这种方式在模板源中调用。

{ $variable|escapeHtml } //no params
{ $variable|escapeHtml( ) } //no params
{ $variable|escapeHtml( 3, 'ISO-8859-1', false) } //indexed params
{ $variable|escapeHtml( flags=3, double_encode=true, encoding='ISO-8859-1') } //intended support for named params
{ $variable|escapeHtml( 'ISO-8859-1', false, flags = 3) } //support for mixed params as in the example

对于上面的过滤器,第一个输入是变量$variable的值,现在最终将由我的模板系统编译成PHP类,看起来像这样。

    $output .= $this->isPrintable(
        $this->_executeCallable( [
            400,/* Token::T_FILTER */
            'escapehtml',/* TagType */
            ['string'=>$this->get('variable'), 'flags'=>3, 'encoding'=>'ISO-8859-1', 'double_encode'=>true ]
        ] ),
        'for variable[ T_VARIABLE_TAG::$variable ] on Line[ 2 ] In Template[ test.tpl ]'
    );

毋庸置疑,我希望能够通过少于3个循环和70多行代码获得答案。

谢谢,

编辑::这与我现在的情况大致相同,少量这是为了节省一些时间,当输入更简单,然后是混合类型或基本上它匹配地图数字索引。此外,我在这里有一些错误检查,因为缺少必需的参数,即。那些不在输入中的映射数组中没有默认值的那些。

    echo '<pre>';

    $map = array(
            'string' => array(
                    'type' => 'string'
            ),
            'flags' => array(
                    'type' => 'integer',
                    'default' => 11
            ),
            'encoding' => array(
                    'type' => 'string',
                    'default' => 'UTF-8'
            ),
            'double_encode' => array(
                    'type' => 'boolean',
                    'default' => true
            )
    );

    $input = array(
            'string' => 'hello',
            0 => 'ISO-8859-1',
            1 => false,
            'flags' => 3
    );

    /* //simple indexed input
    $input = array(
        'hello',
        3,
        'ISO-8859-1',
        false
    );
    */

    $isNumeric = true;
    foreach ( $input as $k => $v){
        if(!is_numeric($k)){
            $isNumeric = false;
        }
    }

    if( !$isNumeric || count( $map ) != count( $input )){
        $numeric = array();
        $assoc = array();
        //split input into associative and indexed keys
        foreach ( $input as $k => $v){
            if(!is_numeric($k)){
                $assoc[$k] = $v;
            }else{
                $numeric[] = $v;
            }
        }

        //setup a dummy array based off the map keys
        $output = array_fill_keys( array_keys( $map ), '_UNSET_');

        foreach( $output as $key => &$value ){
            if( isset( $assoc[ $key ] ) ){
                $value = $assoc[ $key ];
                unset( $assoc[$key] );
            }else if( count( $numeric ) > 0 ){
                $value = array_shift( $numeric );
            }else{
                if( isset( $map[ $key ][ 'default' ]) ){
                    $value = $map[ $key ][ 'default' ];
                }else{
                    //throw error for missing required param
                    die( "missing required param $key on ".__LINE__);
                }
            }
        }

        if( count( $assoc ) > 0 ){
            var_export( $assoc );
            die( "unused inputs on ".__LINE__);
        }

        if( count( $numeric ) > 0 ){
            var_export( $numeric );
            die( "unused inputs on ".__LINE__);
        }
    }else{
        $output = array_combine(array_keys( $map ), $input);
    }


    var_export( $output );

感觉就像有更好的方法来做那件事。

更新2:我应该提到输入可能不包含上面的全部项目,因为在我的模板示例中它可能只是第一个参数。在这种情况下,我必须使用默认值。

更新3:感谢@darp的帮助,稍微修改一下就是我的最终代码。

    echo '<pre>';

    $mapping = array( 
        'string' => array(
                'type' => 'string'
        ),
        'flags' => array(
                'type' => 'integer',
                'default' => 11
        ),
        'encoding' => array(
                'type' => 'string',
                'default' => 'UTF-8'
        ),
        'double_encode' => array(
                'type' => 'boolean',
                'default' => true
        )
    );

    /*$input = array(
            0 => 'test', //string
            'encoding' => 'ISO-8859-1',
            'double_encode' => false,
            1 => 3, //flags
    );*/

    /* //error: missing required param[ string ]
        $input = array(
            'encoding' => 'ISO-8859-1',
            'double_encode' => false,
            'flags' => 3,
    );*/

    /*
    $input = array(  //missing optional flags
            0 => 'test', //string
            'encoding' => 'ISO-8859-1',
            'double_encode' => false,
    );
    */


    $namedParams    = array_intersect_key($input, $mapping);
    $numberedParams = array_diff_key($input, $mapping);
    $unmappedKeys   = array_keys(array_diff_key($mapping, $input));

    $mappedParams = array();
    foreach ( $unmappedKeys as $index => $unmapped ){
        if( isset( $numberedParams[ $index ] ) ){
            $mappedParams[ $unmapped ] = $numberedParams[ $index ];
        }else{
            if( !isset( $mapping[ $unmapped ]['default'] ) ){
                die( "error:missing required param[ {$unmapped} ]on ".__LINE__);
            }else{
                $mappedParams[ $unmapped ] = $mapping[ $unmapped ]['default'];
            }
        }
    }

    $output = array_merge($namedParams, $mappedParams);

    var_export($output);

第一种情况下的输出:

array (
  'encoding' => 'ISO-8859-1',
  'double_encode' => false,
  'string' => 'test',
  'flags' => 3,
)

在第二部分,我得到了一个错误,因为我错过了一个必需的值:

在第三个:

array (
  'encoding' => 'ISO-8859-1',
  'double_encode' => false,
  'string' => 'test',
  'flags' => 11,
)

如果有额外的东西,他们现在只是被忽略了,我应该检查一下,但这并不是什么大不了的事。我只是想避免很多框架都有的魔术黑盒子。我可以添加的唯一另一件事是将它们排序为正确的顺序,但我不确定是否还需要,这取决于我如何实现调用函数等。

无论如何,谢谢,

2 个答案:

答案 0 :(得分:2)

没有测试过,但这应该接近你的想法。

array_merge(
  array_replace(
    array_intersect_key($mapping, $input),
    array_intersect_key($input, $mapping)
  ),
  array_combine(
      array_keys(array_diff_key($mapping, $input)), 
      array_values(array_diff_key($input, $mapping))
  )
)

答案 1 :(得分:1)

这会处理映射,处理缺少的参数,应用缺省值或缺省值:

// Get the named parameters from the input array.
$namedParams = array_intersect_key($input, $mapping);

// Get the mapping keys for those missing from input.
$unmappedKeys = array_keys(array_diff_key($mapping, $input));

// Get the unnamed parameters from the input.
// Pad the array with null placeholders for those missing.
$numberedParams = array_pad(
    array_diff_key($input, $mapping),
    count($unmappedKeys),
    null);

// Map keys to the unnamed parameters.
$mappedParams = array_combine($unmappedKeys, $numberedParams);

// Merge.
$output = array_merge($namedParams, $mappedParams);

// Apply defaults from the $mapping array to parameters with null values.
array_walk(
    $output,
    function (&$v, $k, $m) {
        $v = is_null($v) ? $m[$k]['default'] : $v;
    },
    $mapping
);

var_export($output)

array (
    'string' => 'hello',
    'flags' => 3,
    'encoding' => 'ISO-8859-1',
    'double_encode' => false,
)