PHP - 当给定子键名时,递归地将每个数组元素的键设置为子元素的值

Posted

技术标签:

【中文标题】PHP - 当给定子键名时,递归地将每个数组元素的键设置为子元素的值【英文标题】:PHP - Recursively set each array element's key to the value of a child element when given the childs key name 【发布时间】:2016-02-25 21:58:41 【问题描述】:

我将首先展示一个非递归示例

非递归示例

$given_key_name = 'site_id';

$rows[] = array(
    'site_id' => '0',
    'language_id' => '1',
    'name' => 'sitename',
    'description' =>'site desc',
);

$results = array();
foreach($rows as $row)
    $key_value = $row[$given_key_name];
    unset($row[$given_key_name]);
    $results[$key_value] = $row;


//  OR This method is faster than the forloop

$results = array_combine(array_column($rows, $given_key_name),$rows);
foreach($results as &$row)
    unset($row[$given_key_name]); 

$results等于

$results[0] = array( 
    'language_id' => '1',
    'name' => 'sitename',
    'description' =>'site desc',
);

很简单,键名已设置为给定子元素的值。但我希望能够通过使用多个键名来嵌套和取消嵌套。

例子

$given_key_names = array('site_id', 'language_id');

在这种情况下,所需的结果将是。

$results[0][1] = array( 'name' => 'sitename', 'description' =>'site desc', );

说明

第一个键值已用作 $results 数组中的第一个键,并创建了一个新的空数组作为其值。 $results[0] = array();

由于有第二个键,它的值被设置为新创建的数组的键,它的值也是一个新的空数组。 $results[0][1] = array();

由于没有更多的键,空数组将填充剩余的值

$results[0][1] = array( 'name' => 'sitename', 'description' =>'site desc', );

所以我想要两个函数 nestByKeyNamesunNestByKeyName

NestByKeyNames 函数

Christians Answer solves this

function nestByKeyNames($arrayRows, $arrayKeyOrder)

    // Prepare resulting array
    $arrayResult = array();

    // Cycle the input array
    foreach($arrayRows as $someRow)
        // We will acomplish this using references
        $current = &$arrayResult;

        // get the current level
        foreach($arrayKeyOrder as $someKey)
            $someValue = $someRow[$someKey];
            if(isset($current[$someValue]))
                $current = &$current[$someValue];
            else
                $current[$someValue] = array();
                $current = &$current[$someValue];
            
            unset($someRow[$someKey]);
        
        $current = $someRow;
    
    return $arrayResult;

我想知道是否可以使用array_combine(array_column($arrayRows, $key_name),$arrayRows); 代替第一次迭代来提高性能?

这表示 mysql select 语句的结果。

$rows = array(
    array(
        'pri_id_1' =>1,
        'pri_id_2' =>1,
        'pri_id_3' =>1,
        'col_1' =>'col_value_1111',
        'col_2' =>'col_value_1112',
        'col_3' =>'col_value_1113',
    ),
    array(
        'pri_id_1' =>1,
        'pri_id_2' =>2,
        'pri_id_3' =>1,
        'col_1' =>'col_value_1211',
        'col_2' =>'col_value_1212',
        'col_3' =>'col_value_1213',
    ),
    array(
        'pri_id_1' =>1,
        'pri_id_2' =>3,
        'pri_id_3' =>1,
        'col_1' =>'col_value_1311',
        'col_2' =>'col_value_1312',
        'col_3' =>'col_value_1313',
    )
);

$keyNames = array('pri_id_1','pri_id_2','pri_id_3');
$results = nestByKeyNames($rows, $keyNames);

产生以下输出

Array
(
    [1] => Array
        (
            [1] => Array
                (
                    [1] => Array
                        (
                            [col_1] => col_value_1111
                            [col_2] => col_value_1112
                            [col_3] => col_value_1113
                        )

                )

            [2] => Array
                (
                    [1] => Array
                        (
                            [col_1] => col_value_1211
                            [col_2] => col_value_1212
                            [col_3] => col_value_1213
                        )

                )

            [3] => Array
                (
                    [1] => Array
                        (
                            [col_1] => col_value_1311
                            [col_2] => col_value_1312
                            [col_3] => col_value_1313
                        )

                )

        )

)

UnNestByKeyNames 函数

unNestByKeyNames 应该能够获取此输出并将其转换回原始数组,前提是为其提供键名。 Christians Answer 没有解决这个问题,因为它不能使用单个键名,但我可以说它非常接近。

function unNestByKeyNames($arrayRows, $arrayKeyOrder)




$keyNames = array('pri_id_1','pri_id_2','pri_id_3');
$rows = unNestKeyNames($results, $keyNames);

我的真正目标是从 MYSQL SELECT 语句中获取结果,并使用 nestByKeyNames 使用相同的命名约定填充表单。

例如

<input name="rows[1][1][1][col_1]" value="col_value_1" />

然后首先使用unNestByKeyNames 将$_POST 请求转换回MYSQL INSERT 语句。

据此,我将创建一个INSERT 语句。

function returnValues($rows, $column_names)

    //validation has been removed for clarity

    $implode_VALUES = array();

    foreach ($rows as $key => $row) 
        $implode_row_values = array();
        foreach ($column_names as $column_name) 
            $implode_row_values[$column_name] = $row[$column_name];
        
        if($implode_row_values)
            $implode_VALUES[] = " ('" . implode("','", $implode_row_values) . "') ";
        
    
    return $implode_VALUES;


$implode_COLUMNS = array('pri_id_1','pri_id_2','pri_id_3','col_1','col_2','col_3');

$implode_VALUES = returnValues($rows, $implode_COLUMNS)

$sql = "INSERT INTO table_name (" . implode(',', $implode_COLUMNS) . ") VALUES " . implode(',', $implode_VALUES);

最终结果应该会产生这样的sql语句

INSERT INTO table_name (pri_id_1,pri_id_2,pri_id_3,col_1,col_2,col_3) VALUES ('1','1','1','NEW_value_1111','NEW_value_1112','NEW_value_1113') , ('1','2','1','NEW_value_1211','NEW_value_1212','NEW_value_1213') , ('1','3','1','NEW_value_1311','NEW_value_1312','NEW_value_1313')

我想要什么

关于“nestByKeyNames”功能的改进建议(性能/是否有错误) 帮助生成“unNestByKeyNames”代码 关于我的“$rows to mysql INSERT”方法的改进建议 如何让我的任何代码执行得更好的示例。

【问题讨论】:

【参考方案1】:

这比我最初想象的要棘手,但我相信我有一个混乱的解决方案。

首先,这是我正在处理的数据。 dumpr 是一个自定义函数,可以更好地格式化 var_dump

$arrayKeyOrder = array(
    'site_id',
    'language_id'
);

$original = array(
    array(
        'site_id' => '0',
        'language_id' => '1',
        'name' => 'sitename',
        'description' =>'site desc',
    ),

    array(
        'site_id' => '0',
        'language_id' => '2',
        'name' => 'sitename',
        'description' =>'site desc',
    ),

    array(
        'site_id' => '1',
        'language_id' => '1',
        'name' => 'sitename',
        'description' =>'site desc',
    ),

    array(
        'site_id' => '2',
        'language_id' => '1',
        'name' => 'sitename',
        'description' =>'site desc',
    ),
);

$zipped = doZip($original, $arrayKeyOrder);
$unzipped = unZip($zipped, $arrayKeyOrder);

dumpr($original);
dumpr($zipped);
dumpr($unzipped);

这里是 zip 和 unzip 函数:

function doZip($arrayRows, $arrayKeyOrder)

    // Prepare resulting array
    $arrayResult = array();

    // Cycle the input array
    foreach($arrayRows as $someRow)
        // We will acomplish this using references
        $current = &$arrayResult;

        // get the current level
        foreach($arrayKeyOrder as $someKey)
            $someValue = $someRow[$someKey];
            if(isset($current[$someValue]))
                $current = &$current[$someValue];
            else
                $current[$someValue] = array();
                $current = &$current[$someValue];
            
            unset($someRow[$someKey]);
        

        $current = $someRow;
    

    return $arrayResult;



function unZip($arrayRows, $arrayKeyOrder, $arrayValues = array(), $depth = 0)

    $arrayResults = array();

    if($depth < count($arrayKeyOrder))
        foreach($arrayRows as $key => $value)
            $arrayValues[$depth] = $key;
            $arrayResults[] =  unZip($value, $arrayKeyOrder, $arrayValues, $depth + 1);
        
    else
        $extra = array_combine($arrayKeyOrder, $arrayValues);
        $result = array_merge($extra, $arrayRows);
        return $result;
    

    if($depth == 0)
        for($i = 1; $i < count($arrayKeyOrder); $i++)
            $arrayResults = call_user_func_array('array_merge', $arrayResults);
                
    

    return $arrayResults;

最后,这是输出。让我知道这是否是您所要求的,以及它在更大的数据集上是否可以正常工作。

/vhost/virtual/sandbox/public/index.php:54
array(4) 
    [0] = array(4) 
        [site_id] = string(1) "0"
        [language_id] = string(1) "1"
        [name] = string(8) "sitename"
        [description] = string(9) "site desc"
    
    [1] = array(4) 
        [site_id] = string(1) "0"
        [language_id] = string(1) "2"
        [name] = string(8) "sitename"
        [description] = string(9) "site desc"
    
    [2] = array(4) 
        [site_id] = string(1) "1"
        [language_id] = string(1) "1"
        [name] = string(8) "sitename"
        [description] = string(9) "site desc"
    
    [3] = array(4) 
        [site_id] = string(1) "2"
        [language_id] = string(1) "1"
        [name] = string(8) "sitename"
        [description] = string(9) "site desc"
    


/vhost/virtual/sandbox/public/index.php:55
array(3) 
    [0] = array(2) 
        [1] = array(2) 
            [name] = string(8) "sitename"
            [description] = string(9) "site desc"
        
        [2] = array(2) 
            [name] = string(8) "sitename"
            [description] = string(9) "site desc"
        
    
    [1] = array(1) 
        [1] = array(2) 
            [name] = string(8) "sitename"
            [description] = string(9) "site desc"
        
    
    [2] = array(1) 
        [1] = array(2) 
            [name] = string(8) "sitename"
            [description] = string(9) "site desc"
        
    


/vhost/virtual/sandbox/public/index.php:56
array(4) 
    [0] = array(4) 
        [site_id] = int(1) 0
        [language_id] = int(1) 1
        [name] = string(8) "sitename"
        [description] = string(9) "site desc"
    
    [1] = array(4) 
        [site_id] = int(1) 0
        [language_id] = int(1) 2
        [name] = string(8) "sitename"
        [description] = string(9) "site desc"
    
    [2] = array(4) 
        [site_id] = int(1) 1
        [language_id] = int(1) 1
        [name] = string(8) "sitename"
        [description] = string(9) "site desc"
    
    [3] = array(4) 
        [site_id] = int(1) 2
        [language_id] = int(1) 1
        [name] = string(8) "sitename"
        [description] = string(9) "site desc"
    

【讨论】:

刚刚对 doZip 进行了基准测试,它似乎工作正常,并且比我的尝试更快,谢谢。我已经测试了 unzip 方法,但它似乎不适用于单个主键。 ideone.com/NVqdeN 我已更新 unZip 函数以使用任意数量的排序键。 注意:如果您不使用所有唯一键作为排序键,那么数据将会丢失。这就是这种排序形式的本质。【参考方案2】:

试试这个:

// initialize your array
$all_rows = array();

// loop through query results
while( $row = $qry->fetch_assoc() )

    // temporarily store these vars for easy use later
    $s_id = $row['site_id'];
    $l_id = $row['language_id'];

    // create an empty array based on site_id and language_id
    $all_rows[ $s_id ][ $l_id ] = array();

    // loop through all columns returned from query
    foreach ( $row as $key => $val )
    
        // if it's not one of the two primary keys, push it to the array
        if ( ! in_array($key, $all_primary_keys) )
        
            $all_rows[ $s_id ][ $l_id ][ $key ] = $val;
        
    

【讨论】:

【参考方案3】:

是否有以下原因不起作用?

$results = array();
while($row = $qry->fetch_assoc())

    $results[$row['site_id']][$row['language_id']] = array(

        'name'  =>  $row['name'],
        'description' => $row['description']

    );


【讨论】:

谢谢,该代码必须适用于多个表。所以我不能指定列名。很抱歉造成混乱。【参考方案4】:

这里有两个简单的函数可以解决您的问题。我没有举任何例子,因为我使用了您的数据以及相同的函数名称和参数。

第一个利用指针解决第一步问题:

function nestByKeyNames($rows, $aKeys) 
    $tab=Array();
    foreach ($rows as &$v) 
            // calculate the pointer position
            $t=&$tab;
            foreach ($aKeys as $v1) 
                    $t=&$t[$v[$v1]];
                    unset($v[$v1]);
            
            // save the value
            $t=$v;
    
    return $tab;

这个使用递归算法并给出反向输出

function unNestByKeyNames($arrayRows, $aKeys)
    $t=Array();
    if (!count($aKeys)) return Array($arrayRows);
    foreach ($arrayRows as $k=>&$v) 
            $res=unNestByKeyNames($v, array_slice($aKeys,1));
            foreach ($res as $k1=>$v1) $t[]=array_merge(Array($aKeys[0]=>$k), $v1);
    
    return $t;
 

只要您处理 sql 注入,我对您的 SQL INSERT 方法没有任何建议,我想这可能是您评论“为清楚起见已删除验证”的原因

【讨论】:

【参考方案5】:

如果你想使用主键,你没有真正的方法,你必须知道主键的列名,你不应该查询的列。最好的方法是在 MySQL Query 中使用 AS 关键字

SELECT primary as ID, ... 其中 primary 是您的主键的列名,现在 ID 是您在结果集中的主键。

然后你就可以做标准了

$sortedResults = array();
while($row = $queryResult->fetch_assoc())
    $rowId = $row["ID"];
    $sortedResults[$rowId] = $row;

如果你不知道主键是什么我没有合理的方法来获取它有一种方法来获取表列然后你可以去虽然他们找到主键保存它然后你有主键做你的,但这对你做的每一个查询来说都是一个地狱般的开销。

【讨论】:

可能是因为它难以阅读:没有标点符号、未完成的句子、打字错误……

以上是关于PHP - 当给定子键名时,递归地将每个数组元素的键设置为子元素的值的主要内容,如果未能解决你的问题,请参考以下文章

php数组的重复值如何过滤掉

php中数组可以使用哪些键名

php 二维数组中取某个唯一的键值为键名。(通过合并两个数组来创建一个新数组,其中的一个数组元素为键名,另一个数组的元素为键值。)

php array_merge 与 + 区别

PHP树-不需要递归

PHP数组函数