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

PHP - Recursively set each array element's key to the value of a child element when given the childs key name

我将从展示一个非递归示例开始

非递归示例

$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 函数

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 应该能够获取此输出并将其转换回原始数组,前提是它被赋予了键名。 没有解决这个问题,因为它不适用于单个键名,但我可以看出它非常接近。

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')

我想要什么

试试这个:

// 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;
        }
    }
}

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

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

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

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

    );

}

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

首先,这是我正在处理的数据。 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);

这里是压缩和解压缩函数:

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"
    }
}

如果您想使用主键,则没有真正的方法可以满足您的需求,您必须知道主键的列名,但您不应该知道您查询的列。最好的方法是在 MySQL 查询

中使用 AS 关键字

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

然后你就可以做标准的

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

如果您不知道那里的主键是什么,我没有合理的方法来获取它,有一种方法可以获取 table 列,然后您可以通过它们找到主键并保存它然后你就有了主键来完成你的工作,但这对你进行的每个查询来说都是一笔巨大的开销。

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

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

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 INSERT 方法没有任何建议,只要你注意 sql 注入,我想这可能是你发表评论的原因 "validation has been removed for clarity"